VirtualBox

source: vbox/trunk/src/VBox/Main/xml/cfgldr.cpp@ 6085

Last change on this file since 6085 was 6071, checked in by vboxsync, 17 years ago

Explicitly init the utc offset field too.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 77.8 KB
Line 
1/** @file
2 *
3 * CFGLDR - Configuration Loader
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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
18/* Define STANDALONE_TEST for making a executable that
19 * will load and parse a XML file
20 */
21// #define STANDALONE_TEST
22
23/** @page pg_cfgldr CFGLDR - The Configuration Loader
24 *
25 * The configuration loader loads and keeps the configuration of VBox
26 * session. It will be used by VBox components to retrieve configuration
27 * values at startup and to change values if necessary.
28 *
29 * When VBox is started, it must call CFGLDRLoad to load global
30 * VBox configuration and then VM configuration.
31 *
32 * Handles then used by VBox components to retrieve configuration
33 * values. Components must query their values at startup.
34 * Configuration values can be changed only by respective
35 * component and the component must call CFGLDRSet*, to change the
36 * value in configuration file.
37 *
38 * Values are accessed only by name. It is important for components to
39 * use CFGLDRQuery* only at startup for performance reason.
40 *
41 * All CFGLDR* functions that take a CFGHANDLE or CFGNODE as their first
42 * argument return the VERR_INVALID_HANDLE status code if the argument is
43 * null handle (i.e. its value is zero).
44 */
45
46#define VBOX_XML_WRITER_FILTER
47#define VBOX_XML_XSLT
48#define _CRT_SECURE_NO_DEPRECATE
49
50#define LOG_GROUP LOG_GROUP_MAIN
51#include <VBox/log.h>
52
53#include <VBox/err.h>
54
55#include <iprt/string.h>
56#include <iprt/uuid.h>
57#include <iprt/path.h>
58#include <iprt/file.h>
59#include <iprt/time.h>
60#include <iprt/alloc.h>
61
62#include <xercesc/util/PlatformUtils.hpp>
63
64#include <xercesc/dom/DOM.hpp>
65#include <xercesc/dom/DOMImplementation.hpp>
66#include <xercesc/dom/DOMImplementationLS.hpp>
67#include <xercesc/dom/DOMBuilder.hpp>
68#include <xercesc/dom/DOMWriter.hpp>
69
70#include <xercesc/framework/LocalFileFormatTarget.hpp>
71
72#include <xercesc/util/XMLUni.hpp>
73#include <xercesc/util/XMLUniDefs.hpp>
74#include <xercesc/util/BinInputStream.hpp>
75#include <xercesc/util/BinMemInputStream.hpp>
76
77#ifdef VBOX_XML_WRITER_FILTER
78#include <xercesc/dom/DOMWriterFilter.hpp>
79#endif
80
81#ifdef VBOX_XML_XSLT
82
83#include <xalanc/Include/PlatformDefinitions.hpp>
84#include <xalanc/XalanTransformer/XalanTransformer.hpp>
85#include <xalanc/XalanTransformer/XercesDOMWrapperParsedSource.hpp>
86#include <xalanc/XercesParserLiaison/XercesParserLiaison.hpp>
87#include <xalanc/XercesParserLiaison/XercesDOMSupport.hpp>
88#include <xalanc/XercesParserLiaison/FormatterToXercesDOM.hpp>
89#include <xalanc/XSLT/ProblemListener.hpp>
90
91XALAN_CPP_NAMESPACE_USE
92
93// generated file
94#include "SettingsConverter_xsl.h"
95
96#endif // VBOX_XML_XSLT
97
98// Little hack so we don't have to include the COM stuff
99// Note that those defines need to be made before we include
100// cfgldr.h so that the prototypes can be compiled.
101#ifndef BSTR
102#define OLECHAR wchar_t
103#define BSTR void*
104#if defined(RT_OS_WINDOWS)
105extern "C" BSTR __stdcall SysAllocString(const OLECHAR* sz);
106#else
107extern "C" BSTR SysAllocString(const OLECHAR* sz);
108#endif
109#endif
110
111#include "Logging.h"
112
113#include <VBox/cfgldr.h>
114
115#include <string.h>
116#include <stdio.h> // for sscanf
117
118#ifdef STANDALONE_TEST
119# include <stdlib.h>
120# include <iprt/runtime.h>
121#endif
122
123XERCES_CPP_NAMESPACE_USE
124
125// Helpers
126////////////////////////////////////////////////////////////////////////////////
127
128inline unsigned char fromhex (RTUTF16 hexdigit)
129{
130 if (hexdigit >= '0' && hexdigit <= '9')
131 return hexdigit - '0';
132 if (hexdigit >= 'A' && hexdigit <= 'F')
133 return hexdigit - 'A' + 0xA;
134 if (hexdigit >= 'a' && hexdigit <= 'f')
135 return hexdigit - 'a' + 0xa;
136
137 return 0xFF; // error indicator
138}
139
140inline RTUTF16 tohex (unsigned char ch)
141{
142 return (ch < 0xA) ? ch + '0' : ch - 0xA + 'A';
143}
144
145/**
146 * Converts a string of hex digits to memory bytes.
147 *
148 * @param puszValue String of hex digits to convert.
149 * @param pvValue Where to store converted bytes.
150 * @param cbValue Size of the @a pvValue array.
151 * @param pcbValue Where to store the actual number of stored bytes.
152 *
153 * @return IPRT status code.
154 */
155int wstr_to_bin (PCRTUTF16 puszValue, void *pvValue, unsigned cbValue, unsigned *pcbValue)
156{
157 int rc = VINF_SUCCESS;
158
159 unsigned count = 0;
160 unsigned char *dst = (unsigned char *) pvValue;
161
162 while (*puszValue)
163 {
164 unsigned char b = fromhex (*puszValue);
165
166 if (b == 0xFF)
167 {
168 /* it was not a valid hex digit */
169 rc = VERR_CFG_INVALID_FORMAT;
170 break;
171 }
172
173 if (count < cbValue)
174 {
175 *dst = b;
176 }
177
178 puszValue++;
179
180 if (!*puszValue)
181 {
182 rc = VERR_CFG_INVALID_FORMAT;
183 break;
184 }
185
186 b = fromhex (*puszValue++);
187
188 if (b == 0xFF)
189 {
190 /* it was not a valid hex digit */
191 rc = VERR_CFG_INVALID_FORMAT;
192 break;
193 }
194
195 if (count < cbValue)
196 {
197 *dst = ((*dst) << 4) + b;
198 dst++;
199 }
200
201 count++;
202 }
203
204 *pcbValue = count;
205
206 return rc;
207}
208
209/**
210 * Converts memory bytes to a null-terminated string of hex values.
211 *
212 * @param pvValue Memory array to convert.
213 * @param cbValue Number of bytes in the @a pvValue array.
214 * @param puszValue Where to store the pointer to the resulting string.
215 * On success, this string should be freed using RTUtf16Free().
216 *
217 * @return IPRT status code.
218 */
219static int bin_to_wstr (const void *pvValue, unsigned cbValue, PRTUTF16 *puszValue)
220{
221 int rc = VINF_SUCCESS;
222
223 /* each byte will produce two hex digits and there will be nul
224 * terminator */
225 *puszValue = (PRTUTF16) RTMemTmpAlloc (sizeof (RTUTF16) * (cbValue * 2 + 1));
226
227 if (!*puszValue)
228 {
229 rc = VERR_NO_MEMORY;
230 }
231 else
232 {
233 unsigned i = 0;
234 unsigned char *src = (unsigned char *) pvValue;
235 PRTUTF16 dst = *puszValue;
236
237 for (; i < cbValue; i++, src++)
238 {
239 *dst++ = tohex ((*src) >> 4);
240 *dst++ = tohex ((*src) & 0xF);
241 }
242
243 *dst = '\0';
244 }
245
246 return rc;
247}
248
249// CfgNode
250////////////////////////////////////////////////////////////////////////////////
251
252class CfgNode
253{
254 private:
255 friend class CfgLoader;
256 CfgLoader *pConfiguration;
257 CfgNode *next;
258 CfgNode *prev;
259
260 DOMNode *pdomnode;
261
262 CfgNode (CfgLoader *pcfg);
263 virtual ~CfgNode ();
264
265 int resolve (DOMNode *root, const char *pszName, unsigned uIndex, unsigned flags);
266
267 int getValueString (const char *pszName, PRTUTF16 *ppwszValue);
268 int setValueString (const char *pszName, PRTUTF16 pwszValue);
269
270 DOMNode *findChildText (void);
271
272 public:
273
274 enum {
275 fSearch = 0,
276 fCreateIfNotExists = 1,
277 fAppend = 2
278 };
279
280 static int ReleaseNode (CfgNode *pnode);
281 static int DeleteNode (CfgNode *pnode);
282
283 int CreateChildNode (const char *pszName, CfgNode **ppnode);
284 int AppendChildNode (const char *pszName, CfgNode **ppnode);
285
286 int GetChild (const char *pszName, unsigned uIndex, CfgNode **ppnode);
287 int CountChildren (const char *pszChildName, unsigned *pCount);
288
289
290 int QueryUInt32 (const char *pszName, uint32_t *pulValue);
291 int SetUInt32 (const char *pszName, uint32_t ulValue, unsigned int uiBase = 0);
292 int QueryUInt64 (const char *pszName, uint64_t *pullValue);
293 int SetUInt64 (const char *pszName, uint64_t ullValue, unsigned int uiBase = 0);
294
295 int QueryInt32 (const char *pszName, int32_t *plValue);
296 int SetInt32 (const char *pszName, int32_t lValue, unsigned int uiBase = 0);
297 int QueryInt64 (const char *pszName, int64_t *pllValue);
298 int SetInt64 (const char *pszName, int64_t llValue, unsigned int uiBase = 0);
299
300 int QueryUInt16 (const char *pszName, uint16_t *puhValue);
301 int SetUInt16 (const char *pszName, uint16_t uhValue, unsigned int uiBase = 0);
302
303 int QueryBin (const char *pszName, void *pvValue, unsigned cbValue, unsigned *pcbValue);
304 int SetBin (const char *pszName, const void *pvValue, unsigned cbValue);
305 int QueryString (const char *pszName, void **pValue, unsigned cbValue, unsigned *pcbValue, bool returnUtf16);
306 int SetString (const char *pszName, const char *pszValue, unsigned cbValue, bool isUtf16);
307
308 int QueryBool (const char *pszName, bool *pfValue);
309 int SetBool (const char *pszName, bool fValue);
310
311 int DeleteAttribute (const char *pszName);
312};
313
314// CfgLoader
315////////////////////////////////////////////////////////////////////////////////
316
317class CfgLoader
318{
319 private:
320
321 friend class CfgNode;
322
323 PRTUTF16 pwszOriginalFilename;
324 RTFILE hOriginalFileHandle; /** r=bird: this is supposed to mean 'handle to the orignal file handle'? The name is
325 * overloaded with too many 'handles'... That goes for those hFileHandle parameters too.
326 * hOriginalFile / FileOriginal and hFile / File should do by a long way. */
327 CfgNode *pfirstnode;
328
329 DOMBuilder *builder;
330 DOMNode *root;
331
332 int getNode (DOMNode *prootnode, const char *pszName, unsigned uIndex, CfgNode **ppnode, unsigned flags);
333
334 DOMDocument *Document(void) { return static_cast<DOMDocument *>(root); };
335
336 public:
337
338 CfgLoader ();
339 virtual ~CfgLoader ();
340
341 int Load (const char *pszFileName, RTFILE hFileHandle,
342 const char *pszExternalSchemaLocation, bool bDoNamespaces,
343 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
344 char **ppszErrorMessage);
345 int Save (const char *pszFilename, RTFILE hFileHandle,
346 char **ppszErrorMessage);
347 int Create ();
348#ifdef VBOX_XML_XSLT
349 int Transform (const char *pszTemlateLocation,
350 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
351 char **ppszErrorMessage);
352#endif
353
354 static int FreeConfiguration (CfgLoader *pcfg);
355
356 int CreateNode (const char *pszName, CfgNode **ppnode);
357
358 int GetNode (const char *pszName, unsigned uIndex, CfgNode **ppnode);
359};
360
361// VBoxWriterFilter
362////////////////////////////////////////////////////////////////////////////////
363
364#ifdef VBOX_XML_WRITER_FILTER
365class VBoxWriterFilter : public DOMWriterFilter
366{
367 public:
368 VBoxWriterFilter (unsigned long whatToShow = DOMNodeFilter::SHOW_ALL);
369 ~VBoxWriterFilter () {};
370
371 virtual short acceptNode (const DOMNode*) const;
372 virtual unsigned long getWhatToShow () const { return fWhatToShow; };
373 virtual void setWhatToShow (unsigned long toShow) { fWhatToShow = toShow; };
374
375 private:
376 unsigned long fWhatToShow;
377};
378
379VBoxWriterFilter::VBoxWriterFilter(unsigned long whatToShow)
380 :
381 fWhatToShow (whatToShow)
382{
383}
384
385short VBoxWriterFilter::acceptNode(const DOMNode* node) const
386{
387 switch (node->getNodeType())
388 {
389 case DOMNode::TEXT_NODE:
390 {
391 /* Reject empty nodes */
392 const XMLCh *pxmlch = node->getNodeValue ();
393
394 if (pxmlch)
395 {
396 while (*pxmlch != chNull)
397 {
398 if ( *pxmlch == chLF
399 || *pxmlch == chCR
400 || *pxmlch == chSpace
401 || *pxmlch == chHTab
402 )
403 {
404 pxmlch++;
405 continue;
406 }
407
408 break;
409 }
410
411 if (*pxmlch != chNull)
412 {
413 /* Accept the node because it contains non space characters */
414 return DOMNodeFilter::FILTER_ACCEPT;
415 }
416 }
417
418 return DOMNodeFilter::FILTER_REJECT;
419 }
420 }
421
422 return DOMNodeFilter::FILTER_ACCEPT;
423}
424
425#endif
426
427// CfgLdrInputSource
428////////////////////////////////////////////////////////////////////////////////
429
430/**
431 * A wrapper around RTFILE or a char buffer that acts like a DOMInputSource
432 * and therefore can be with DOMBuilder instances.
433 */
434class CfgLdrInputSource : public DOMInputSource
435{
436public:
437
438 CfgLdrInputSource (PCFGLDRENTITY pcEntity, const char *pcszSystemId);
439 virtual ~CfgLdrInputSource() { release(); }
440
441 // Functions introduced in DOM Level 3
442
443 const XMLCh *getEncoding () const { return NULL; }
444 const XMLCh *getPublicId () const { return NULL; }
445 const XMLCh *getSystemId () const { return (const XMLCh *)m_pwszSystemId; }
446 const XMLCh *getBaseURI () const { return (const XMLCh *)m_pwszBaseURI; }
447
448 void setEncoding (const XMLCh *const /* encodingStr */)
449 { AssertMsgFailed (("Not implemented!\n")); }
450 void setPublicId (const XMLCh *const /* publicId */)
451 { AssertMsgFailed (("Not implemented!\n")); }
452 void setSystemId (const XMLCh *const /* systemId */)
453 { AssertMsgFailed (("Not implemented!\n")); }
454 void setBaseURI (const XMLCh *const /* baseURI */)
455 { AssertMsgFailed (("Not implemented!\n")); }
456
457 // Non-standard Extension
458
459 BinInputStream *makeStream() const;
460 void setIssueFatalErrorIfNotFound (const bool /* flag */) {}
461 bool getIssueFatalErrorIfNotFound() const { return true; }
462 void release();
463
464private:
465
466 class FileHandleInputStream : public BinInputStream
467 {
468 public:
469
470 FileHandleInputStream (RTFILE hFileHandle);
471
472 unsigned int curPos() const;
473 unsigned int readBytes (XMLByte *const toFill, const unsigned int maxToRead);
474
475 private:
476
477 RTFILE m_hFileHandle;
478 uint64_t m_cbPos;
479 };
480
481 void init (const char *pcszSystemId);
482
483 CFGLDRENTITY m_entity;
484
485 PRTUTF16 m_pwszSystemId;
486 PRTUTF16 m_pwszBaseURI;
487};
488
489CfgLdrInputSource::CfgLdrInputSource (PCFGLDRENTITY pcEntity,
490 const char *pcszSystemId) :
491 m_pwszSystemId (NULL), m_pwszBaseURI (NULL)
492{
493 Assert (pcEntity && pcEntity->enmType != CFGLDRENTITYTYPE_INVALID);
494 // make a copy of the entity descriptor
495 m_entity = *pcEntity;
496
497 Assert (pcszSystemId);
498 int rc = RTStrToUtf16 (pcszSystemId, &m_pwszSystemId);
499 AssertRC (rc);
500
501 char *pszBaseURI = NULL;
502 pszBaseURI = RTStrDup (pcszSystemId);
503 Assert (pszBaseURI);
504 RTPathStripFilename (pszBaseURI);
505
506 rc = RTStrToUtf16 (pszBaseURI, &m_pwszBaseURI);
507 AssertRC (rc);
508
509 RTStrFree(pszBaseURI);
510}
511
512void CfgLdrInputSource::release()
513{
514 switch (m_entity.enmType)
515 {
516 case CFGLDRENTITYTYPE_HANDLE:
517 if (m_entity.u.handle.bClose)
518 RTFileClose (m_entity.u.handle.hFile);
519 break;
520 case CFGLDRENTITYTYPE_MEMORY:
521 if (m_entity.u.memory.bFree)
522 RTMemTmpFree (m_entity.u.memory.puchBuf);
523 break;
524 default:
525 break;
526 };
527
528 m_entity.enmType = CFGLDRENTITYTYPE_INVALID;
529
530 if (m_pwszBaseURI)
531 {
532 RTUtf16Free (m_pwszBaseURI);
533 m_pwszBaseURI = NULL;
534 }
535 if (m_pwszSystemId)
536 {
537 RTUtf16Free (m_pwszSystemId);
538 m_pwszSystemId = NULL;
539 }
540}
541
542BinInputStream *CfgLdrInputSource::makeStream() const
543{
544 BinInputStream *stream = NULL;
545
546 switch (m_entity.enmType)
547 {
548 case CFGLDRENTITYTYPE_HANDLE:
549 stream = new FileHandleInputStream (m_entity.u.handle.hFile);
550 break;
551 case CFGLDRENTITYTYPE_MEMORY:
552 // puchBuf is neither copied nor destructed by BinMemInputStream
553 stream = new BinMemInputStream (m_entity.u.memory.puchBuf,
554 m_entity.u.memory.cbBuf,
555 BinMemInputStream::BufOpt_Reference);
556 break;
557 default:
558 AssertMsgFailed (("Invalid resolver entity type!\n"));
559 break;
560 };
561
562 return stream;
563}
564
565CfgLdrInputSource::FileHandleInputStream::FileHandleInputStream (RTFILE hFileHandle) :
566 m_hFileHandle (hFileHandle),
567 m_cbPos (0)
568{
569}
570
571unsigned int CfgLdrInputSource::FileHandleInputStream::curPos() const
572{
573 AssertMsg (!(m_cbPos >> 32), ("m_cbPos exceeds 32 bits (%16Xll)\n", m_cbPos));
574 return (unsigned int)m_cbPos;
575}
576
577unsigned int CfgLdrInputSource::FileHandleInputStream::readBytes (
578 XMLByte *const toFill, const unsigned int maxToRead
579)
580{
581 /// @todo (dmik) trhow the appropriate exceptions if we fail to write
582
583 int rc;
584 NOREF (rc);
585
586 // memorize the current position
587 uint64_t cbOriginalPos = RTFileTell (m_hFileHandle);
588 Assert (cbOriginalPos != ~0ULL);
589 // set the new position
590 rc = RTFileSeek (m_hFileHandle, m_cbPos, RTFILE_SEEK_BEGIN, NULL);
591 AssertRC (rc);
592
593 // read from the file
594 size_t cbRead = 0;
595 rc = RTFileRead (m_hFileHandle, toFill, maxToRead, &cbRead);
596 AssertRC (rc);
597
598 // adjust the private position
599 m_cbPos += cbRead;
600 // restore the current position
601 rc = RTFileSeek (m_hFileHandle, cbOriginalPos, RTFILE_SEEK_BEGIN, NULL);
602 AssertRC (rc);
603
604 return (unsigned int)cbRead;
605}
606
607// CfgLdrFormatTarget
608////////////////////////////////////////////////////////////////////////////////
609
610class CfgLdrFormatTarget : public XMLFormatTarget
611{
612public:
613
614 CfgLdrFormatTarget (PCFGLDRENTITY pcEntity);
615 ~CfgLdrFormatTarget();
616
617 // virtual XMLFormatTarget methods
618 void writeChars (const XMLByte *const toWrite, const unsigned int count,
619 XMLFormatter *const formatter);
620 void flush() {}
621
622private:
623
624 CFGLDRENTITY m_entity;
625};
626
627CfgLdrFormatTarget::CfgLdrFormatTarget (PCFGLDRENTITY pcEntity)
628{
629 Assert (pcEntity && pcEntity->enmType != CFGLDRENTITYTYPE_INVALID);
630 // make a copy of the entity descriptor
631 m_entity = *pcEntity;
632
633 switch (m_entity.enmType)
634 {
635 case CFGLDRENTITYTYPE_HANDLE:
636 int rc;
637 // start writting from the beginning
638 rc = RTFileSeek (m_entity.u.handle.hFile, 0, RTFILE_SEEK_BEGIN, NULL);
639 AssertRC (rc);
640 NOREF (rc);
641 break;
642 case CFGLDRENTITYTYPE_MEMORY:
643 AssertMsgFailed (("Unsupported entity type!\n"));
644 break;
645 default:
646 break;
647 };
648}
649
650CfgLdrFormatTarget::~CfgLdrFormatTarget()
651{
652 switch (m_entity.enmType)
653 {
654 case CFGLDRENTITYTYPE_HANDLE:
655 {
656 int rc;
657 // truncate the file upto the size actually written
658 uint64_t cbPos = RTFileTell (m_entity.u.handle.hFile);
659 Assert (cbPos != ~0ULL);
660 rc = RTFileSetSize (m_entity.u.handle.hFile, cbPos);
661 AssertRC (rc);
662 // reset the position to he beginning
663 rc = RTFileSeek (m_entity.u.handle.hFile, 0, RTFILE_SEEK_BEGIN, NULL);
664 AssertRC (rc);
665 NOREF (rc);
666 if (m_entity.u.handle.bClose)
667 RTFileClose (m_entity.u.handle.hFile);
668 break;
669 }
670 case CFGLDRENTITYTYPE_MEMORY:
671 break;
672 default:
673 break;
674 };
675}
676
677void CfgLdrFormatTarget::writeChars (const XMLByte *const toWrite,
678 const unsigned int count,
679 XMLFormatter *const /* formatter */)
680{
681 /// @todo (dmik) trhow the appropriate exceptions if we fail to write
682
683 switch (m_entity.enmType)
684 {
685 case CFGLDRENTITYTYPE_HANDLE:
686 int rc;
687 rc = RTFileWrite (m_entity.u.handle.hFile, toWrite, count, NULL);
688 AssertRC (rc);
689 NOREF (rc);
690 break;
691 case CFGLDRENTITYTYPE_MEMORY:
692 AssertMsgFailed (("Unsupported entity type!\n"));
693 break;
694 default:
695 AssertMsgFailed (("Invalid entity type!\n"));
696 break;
697 };
698}
699
700// CfgLdrEntityResolver
701////////////////////////////////////////////////////////////////////////////////
702
703/**
704 * A wrapper around FNCFGLDRENTITYRESOLVER callback that acts like a
705 * DOMEntityResolver and therefore can be used with DOMBuilder instances.
706 */
707class CfgLdrEntityResolver : public DOMEntityResolver
708{
709public:
710
711 CfgLdrEntityResolver (PFNCFGLDRENTITYRESOLVER pfnEntityResolver) :
712 m_pfnEntityResolver (pfnEntityResolver) {}
713
714 // Functions introduced in DOM Level 2
715 DOMInputSource *resolveEntity (const XMLCh *const publicId,
716 const XMLCh *const systemId,
717 const XMLCh *const baseURI);
718
719private:
720
721 PFNCFGLDRENTITYRESOLVER m_pfnEntityResolver;
722};
723
724DOMInputSource *CfgLdrEntityResolver::resolveEntity (const XMLCh *const publicId,
725 const XMLCh *const systemId,
726 const XMLCh *const baseURI)
727{
728 if (!m_pfnEntityResolver)
729 return NULL;
730
731 DOMInputSource *source = NULL;
732 int rc = VINF_SUCCESS;
733
734 char *pszPublicId = NULL;
735 char *pszSystemId = NULL;
736 char *pszBaseURI = NULL;
737
738 if (publicId)
739 rc = RTUtf16ToUtf8 (publicId, &pszPublicId);
740 if (VBOX_SUCCESS (rc))
741 {
742 if (systemId)
743 rc = RTUtf16ToUtf8 (systemId, &pszSystemId);
744 if (VBOX_SUCCESS (rc))
745 {
746 if (baseURI)
747 rc = RTUtf16ToUtf8 (baseURI, &pszBaseURI);
748 if (VBOX_SUCCESS (rc))
749 {
750 CFGLDRENTITY entity;
751 rc = m_pfnEntityResolver (pszPublicId, pszSystemId, pszBaseURI,
752 &entity);
753 if (rc == VINF_SUCCESS)
754 source = new CfgLdrInputSource (&entity, pszSystemId);
755 }
756 }
757 }
758
759 if (pszBaseURI)
760 RTStrFree (pszBaseURI);
761 if (pszSystemId)
762 RTStrFree (pszSystemId);
763 if (pszPublicId)
764 RTStrFree (pszPublicId);
765
766 return source;
767}
768
769// CfgLdrErrorHandler
770////////////////////////////////////////////////////////////////////////////////
771
772/**
773 * An error handler that accumulates all error messages in a single UTF-8 string.
774 */
775class CfgLdrErrorHandler : public DOMErrorHandler
776#ifdef VBOX_XML_XSLT
777 , public ProblemListener
778#endif
779{
780public:
781
782 CfgLdrErrorHandler();
783 ~CfgLdrErrorHandler();
784
785 bool hasErrors() { return m_pszBuf != NULL; }
786
787 /** Transfers ownership of the string to the caller and resets the handler */
788 char *takeErrorMessage() {
789 char *pszBuf = m_pszBuf;
790 m_pszBuf = NULL;
791 return pszBuf;
792 }
793
794 // Functions introduced in DOM Level 3
795 bool handleError (const DOMError &domError);
796
797#ifdef VBOX_XML_XSLT
798 // Xalan ProblemListener interface
799 void setPrintWriter (PrintWriter *pw) {}
800 void problem (eProblemSource where, eClassification classification,
801 const XalanNode *sourceNode, const ElemTemplateElement *styleNode,
802 const XalanDOMString &msg, const XalanDOMChar *uri,
803 int lineNo, int charOffset);
804#endif
805
806private:
807
808 char *m_pszBuf;
809};
810
811CfgLdrErrorHandler::CfgLdrErrorHandler() :
812 m_pszBuf (NULL)
813{
814}
815
816CfgLdrErrorHandler::~CfgLdrErrorHandler()
817{
818 if (m_pszBuf)
819 RTMemTmpFree (m_pszBuf);
820}
821
822bool CfgLdrErrorHandler::handleError (const DOMError &domError)
823{
824 const char *pszSeverity = NULL;
825 switch (domError.getSeverity())
826 {
827 case DOMError::DOM_SEVERITY_WARNING: pszSeverity = "WARNING: ";
828 case DOMError::DOM_SEVERITY_ERROR: pszSeverity = "ERROR: ";
829 case DOMError::DOM_SEVERITY_FATAL_ERROR: pszSeverity = "FATAL ERROR: ";
830 }
831
832 char *pszLocation = NULL;
833 const DOMLocator *pLocation = domError.getLocation();
834 if (pLocation)
835 {
836 static const char Location[] = "\nLocation: '%s', line %d, column %d";
837
838 char *pszURI = NULL;
839 if (pLocation->getURI())
840 RTUtf16ToUtf8 (pLocation->getURI(), &pszURI);
841
842 size_t cbLocation = sizeof (Location) +
843 (pszURI ? strlen (pszURI) : 10 /* NULL */) +
844 10 + 10 + 1 /* line & column & \0 */;
845 pszLocation = (char *) RTMemTmpAllocZ (cbLocation);
846 RTStrPrintf (pszLocation, cbLocation, Location,
847 pszURI,
848 pLocation->getLineNumber(), pLocation->getColumnNumber());
849
850 if (pszURI)
851 RTStrFree (pszURI);
852 }
853
854 LogFlow (("CfgLdrErrorHandler::handleError():\n %s%ls%s\n",
855 pszSeverity, domError.getMessage(), pszLocation));
856
857 char *pszMsg = NULL;
858 if (domError.getMessage())
859 RTUtf16ToUtf8 (domError.getMessage(), &pszMsg);
860
861 size_t cbNewBuf = (m_pszBuf ? strlen (m_pszBuf) : 0) +
862 (pszSeverity ? strlen (pszSeverity) : 0) +
863 (pszMsg ? strlen (pszMsg) : 0) +
864 (pszLocation ? strlen (pszLocation) : 0);
865 char *pszNewBuf = (char *) RTMemTmpAllocZ (cbNewBuf + 2 /* \n + \0 */);
866
867 if (m_pszBuf)
868 {
869 strcpy (pszNewBuf, m_pszBuf);
870 strcat (pszNewBuf, "\n");
871 }
872 if (pszSeverity)
873 strcat (pszNewBuf, pszSeverity);
874 if (pszMsg)
875 strcat (pszNewBuf, pszMsg);
876 if (pszLocation)
877 strcat (pszNewBuf, pszLocation);
878
879 if (m_pszBuf)
880 RTMemTmpFree (m_pszBuf);
881 m_pszBuf = pszNewBuf;
882
883 if (pszLocation)
884 RTMemTmpFree (pszLocation);
885 if (pszMsg)
886 RTStrFree (pszMsg);
887
888 // fail on any error when possible
889 return false;
890}
891
892#ifdef VBOX_XML_XSLT
893void CfgLdrErrorHandler::problem (eProblemSource where, eClassification classification,
894 const XalanNode *sourceNode, const ElemTemplateElement *styleNode,
895 const XalanDOMString &msg, const XalanDOMChar *uri,
896 int lineNo, int charOffset)
897{
898 const char *pszClass = NULL;
899 switch (classification)
900 {
901 case eMESSAGE: pszClass = "INFO: ";
902 case eWARNING: pszClass = "WARNING: ";
903 case eERROR: pszClass = "ERROR: ";
904 }
905
906 LogFlow (("CfgLdrErrorHandler::problem():\n %s%ls\n", pszClass, msg.c_str()));
907
908 char *pszMsg = NULL;
909 if (msg.c_str())
910 RTUtf16ToUtf8 (msg.c_str(), &pszMsg);
911
912 size_t cbNewBuf = (m_pszBuf ? strlen (m_pszBuf) : 0) +
913 (pszClass ? strlen (pszClass) : 0) +
914 (pszMsg ? strlen (pszMsg) : 0);
915 char *pszNewBuf = (char *) RTMemTmpAllocZ (cbNewBuf + 2 /* \n + \0 */);
916
917 if (m_pszBuf)
918 {
919 strcpy (pszNewBuf, m_pszBuf);
920 strcat (pszNewBuf, "\n");
921 }
922 if (pszClass)
923 strcat (pszNewBuf, pszClass);
924 if (pszMsg)
925 strcat (pszNewBuf, pszMsg);
926
927 if (m_pszBuf)
928 RTStrFree (m_pszBuf);
929
930 m_pszBuf = RTStrDup (pszNewBuf);
931
932 if (pszNewBuf)
933 RTMemTmpFree (pszNewBuf);
934 if (pszMsg)
935 RTStrFree (pszMsg);
936}
937#endif
938
939//
940////////////////////////////////////////////////////////////////////////////////
941
942static int xmlInitialized = 0;
943
944static int initXML (void)
945{
946 try
947 {
948 XMLPlatformUtils::Initialize();
949 }
950
951 catch(...)
952 {
953 return 0;
954 }
955
956 return (xmlInitialized = 1);
957}
958
959static void terminateXML (void)
960{
961 if (xmlInitialized)
962 {
963 XMLPlatformUtils::Terminate();
964 xmlInitialized = 0;
965 }
966}
967
968/* CfgLoader implementation */
969CfgLoader::CfgLoader ()
970 :
971 pwszOriginalFilename (NULL),
972 hOriginalFileHandle (NIL_RTFILE),
973 pfirstnode (NULL),
974 builder (NULL),
975 root (NULL)
976{
977}
978
979CfgLoader::~CfgLoader ()
980{
981 if (pwszOriginalFilename)
982 {
983 RTUtf16Free (pwszOriginalFilename);
984 }
985
986 if (builder)
987 {
988 /* Configuration was parsed from a file.
989 * Parser owns root and will delete root.
990 */
991 builder->release();
992 }
993 else if (root)
994 {
995 /* This is new, just created configuration.
996 * root is new object created by DOMImplementation::createDocument
997 * We have to delete root.
998 */
999 root->release();
1000 }
1001}
1002
1003int CfgLoader::Load (const char *pszFileName, RTFILE hFileHandle,
1004 const char *pszExternalSchemaLocation, bool bDoNamespaces,
1005 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
1006 char **ppszErrorMessage)
1007{
1008 if (!xmlInitialized)
1009 return VERR_NOT_SUPPORTED;
1010
1011 Assert (!root && !pwszOriginalFilename);
1012 if (root || pwszOriginalFilename)
1013 return VERR_ALREADY_LOADED;
1014
1015 static const XMLCh LS[] = { chLatin_L, chLatin_S, chNull };
1016 DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation (LS);
1017 if (!impl)
1018 return VERR_NOT_SUPPORTED;
1019
1020 // note:
1021 // member variables allocated here (builder, pwszOriginalFilename) are not
1022 // freed in case of error because CFGLDRLoad() that calls this method will
1023 // delete this instance (and all members) if we return a failure from here
1024
1025 builder = static_cast <DOMImplementationLS *> (impl)->
1026 createDOMBuilder (DOMImplementationLS::MODE_SYNCHRONOUS, 0);
1027 if (!builder)
1028 return VERR_NOT_SUPPORTED;
1029
1030 int rc = VINF_SUCCESS;
1031
1032 if (ppszErrorMessage)
1033 *ppszErrorMessage = NULL;
1034
1035 // set parser's features
1036 Assert (builder->canSetFeature (XMLUni::fgDOMDatatypeNormalization, true));
1037 if (builder->canSetFeature (XMLUni::fgDOMDatatypeNormalization, true))
1038 builder->setFeature (XMLUni::fgDOMDatatypeNormalization, true);
1039 else
1040 return VERR_NOT_SUPPORTED;
1041 if (bDoNamespaces)
1042 {
1043 Assert (builder->canSetFeature (XMLUni::fgDOMNamespaces, true));
1044 if (builder->canSetFeature (XMLUni::fgDOMNamespaces, true))
1045 builder->setFeature (XMLUni::fgDOMNamespaces, true);
1046 else
1047 return VERR_NOT_SUPPORTED;
1048 }
1049 if (pszExternalSchemaLocation)
1050 {
1051 // set validation related features & properties
1052 Assert (builder->canSetFeature (XMLUni::fgDOMValidation, true));
1053 if (builder->canSetFeature (XMLUni::fgDOMValidation, true))
1054 builder->setFeature (XMLUni::fgDOMValidation, true);
1055 else
1056 return VERR_NOT_SUPPORTED;
1057 Assert (builder->canSetFeature (XMLUni::fgXercesSchema, true));
1058 if (builder->canSetFeature (XMLUni::fgXercesSchema, true))
1059 builder->setFeature (XMLUni::fgXercesSchema, true);
1060 else
1061 return VERR_NOT_SUPPORTED;
1062 Assert (builder->canSetFeature (XMLUni::fgXercesSchemaFullChecking, true));
1063 if (builder->canSetFeature (XMLUni::fgXercesSchemaFullChecking, true))
1064 builder->setFeature (XMLUni::fgXercesSchemaFullChecking, true);
1065 else
1066 return VERR_NOT_SUPPORTED;
1067
1068 PRTUTF16 pwszExternalSchemaLocation = NULL;
1069 rc = RTStrToUtf16 (pszExternalSchemaLocation, &pwszExternalSchemaLocation);
1070 if (VBOX_FAILURE (rc))
1071 return rc;
1072
1073 if (bDoNamespaces)
1074 {
1075 // set schema that supports namespaces
1076 builder->setProperty (XMLUni::fgXercesSchemaExternalSchemaLocation,
1077 pwszExternalSchemaLocation);
1078 }
1079 else
1080 {
1081 // set schema that doesn't support namespaces
1082 builder->setProperty (XMLUni::fgXercesSchemaExternalNoNameSpaceSchemaLocation,
1083 pwszExternalSchemaLocation);
1084 }
1085
1086 RTUtf16Free (pwszExternalSchemaLocation);
1087 }
1088
1089 hOriginalFileHandle = hFileHandle;
1090 rc = RTStrToUtf16 (pszFileName, &pwszOriginalFilename);
1091 if (VBOX_FAILURE (rc))
1092 return rc;
1093
1094 CfgLdrEntityResolver entityResolver (pfnEntityResolver);
1095 builder->setEntityResolver (&entityResolver);
1096
1097 CfgLdrErrorHandler errorHandler;
1098 builder->setErrorHandler (&errorHandler);
1099
1100 try
1101 {
1102 if (hFileHandle != NIL_RTFILE)
1103 {
1104 CFGLDRENTITY entity;
1105 entity.enmType = CFGLDRENTITYTYPE_HANDLE;
1106 entity.u.handle.hFile = hFileHandle;
1107 entity.u.handle.bClose = false;
1108 CfgLdrInputSource source (&entity, pszFileName);
1109 root = builder->parse (source);
1110 }
1111 else
1112 {
1113 root = builder->parseURI (pwszOriginalFilename);
1114 }
1115 }
1116 catch (...)
1117 {
1118 rc = VERR_OPEN_FAILED;
1119 }
1120
1121 if (errorHandler.hasErrors())
1122 {
1123 // this will transfer ownership of the string
1124 if (ppszErrorMessage)
1125 *ppszErrorMessage = errorHandler.takeErrorMessage();
1126 rc = VERR_OPEN_FAILED;
1127 }
1128
1129 builder->setErrorHandler (NULL);
1130 builder->setEntityResolver (NULL);
1131
1132 return rc;
1133}
1134
1135int CfgLoader::Save (const char *pszFilename, RTFILE hFileHandle,
1136 char **ppszErrorMessage)
1137{
1138 if (!pszFilename && !pwszOriginalFilename &&
1139 hFileHandle == NIL_RTFILE && hOriginalFileHandle == NIL_RTFILE)
1140 {
1141 // no explicit handle/filename specified and the configuration
1142 // was created from scratch
1143 return VERR_INVALID_PARAMETER;
1144 }
1145
1146 static const XMLCh LS[] = { chLatin_L, chLatin_S, chNull };
1147 DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation (LS);
1148 if (!impl)
1149 return VERR_NOT_SUPPORTED;
1150
1151 DOMWriter *writer = static_cast <DOMImplementationLS *> (impl)->createDOMWriter();
1152 if (!writer)
1153 return VERR_NOT_SUPPORTED;
1154
1155 int rc = VINF_SUCCESS;
1156
1157 if (ppszErrorMessage)
1158 *ppszErrorMessage = NULL;
1159
1160#ifdef VBOX_XML_WRITER_FILTER
1161 VBoxWriterFilter theFilter (DOMNodeFilter::SHOW_TEXT);
1162 writer->setFilter (&theFilter);
1163#endif
1164
1165 writer->setEncoding (XMLUni::fgUTF8EncodingString);
1166
1167 // set feature if the serializer supports the feature/mode
1168 if (writer->canSetFeature(XMLUni::fgDOMWRTDiscardDefaultContent, true))
1169 writer->setFeature(XMLUni::fgDOMWRTDiscardDefaultContent, true);
1170 if (writer->canSetFeature (XMLUni::fgDOMWRTFormatPrettyPrint, true))
1171 writer->setFeature (XMLUni::fgDOMWRTFormatPrettyPrint, true);
1172
1173 CfgLdrErrorHandler errorHandler;
1174 writer->setErrorHandler (&errorHandler);
1175
1176 try
1177 {
1178 if (hFileHandle != NIL_RTFILE || hOriginalFileHandle != NIL_RTFILE)
1179 {
1180 CFGLDRENTITY entity;
1181 entity.enmType = CFGLDRENTITYTYPE_HANDLE;
1182 entity.u.handle.hFile = hFileHandle != NIL_RTFILE ? hFileHandle :
1183 hOriginalFileHandle;
1184 entity.u.handle.bClose = false;
1185 CfgLdrFormatTarget target (&entity);
1186 writer->writeNode (&target, *root);
1187 }
1188 else
1189 {
1190 PRTUTF16 pwszFilename = NULL;
1191 if (pszFilename)
1192 rc = RTStrToUtf16 (pszFilename, &pwszFilename);
1193 if (VBOX_SUCCESS (rc))
1194 {
1195 LocalFileFormatTarget target (pwszFilename ? pwszFilename :
1196 pwszOriginalFilename);
1197 if (pwszFilename)
1198 RTUtf16Free (pwszFilename);
1199
1200 writer->writeNode (&target, *root);
1201 }
1202 }
1203 }
1204 catch(...)
1205 {
1206 rc = VERR_FILE_IO_ERROR;
1207 }
1208
1209 if (errorHandler.hasErrors())
1210 {
1211 // this will transfer ownership of the string
1212 if (ppszErrorMessage)
1213 *ppszErrorMessage = errorHandler.takeErrorMessage();
1214 rc = VERR_FILE_IO_ERROR;
1215 }
1216
1217 writer->release();
1218
1219 if (hFileHandle != NIL_RTFILE || hOriginalFileHandle != NIL_RTFILE)
1220 (void)RTFileFlush(hFileHandle != NIL_RTFILE ? hFileHandle :
1221 hOriginalFileHandle);
1222
1223 return rc;
1224}
1225
1226#ifdef VBOX_XML_XSLT
1227int CfgLoader::Transform (const char *pszTemlateLocation,
1228 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
1229 char **ppszErrorMessage)
1230{
1231 AssertReturn (strcmp (pszTemlateLocation, "SettingsConverter.xsl") == 0,
1232 VERR_NOT_SUPPORTED);
1233 AssertReturn (pfnEntityResolver == NULL,
1234 VERR_NOT_SUPPORTED);
1235
1236 int rc = VINF_SUCCESS;
1237
1238 if (ppszErrorMessage)
1239 *ppszErrorMessage = NULL;
1240
1241 XalanTransformer::initialize();
1242
1243 XalanTransformer xalan;
1244
1245 // input stream to read from SettingsConverter_xsl
1246 struct SettingsConverterStream : public XSLTInputSource
1247 {
1248 SettingsConverterStream()
1249 {
1250 XMLCh *id = XMLString::transcode ("SettingsConverter.xsl");
1251 setSystemId (id);
1252 setPublicId (id);
1253 XMLString::release (&id);
1254 }
1255
1256 BinInputStream *makeStream () const
1257 {
1258 return new BinMemInputStream (g_abSettingsConverter_xsl,
1259 g_cbSettingsConverter_xsl);
1260 }
1261 };
1262
1263 CfgLdrErrorHandler errorHandler;
1264 xalan.setProblemListener (&errorHandler);
1265
1266 try
1267 {
1268 // target is the DOM tree
1269 DOMDocument *newRoot =
1270 DOMImplementation::getImplementation()->createDocument();
1271 FormatterToXercesDOM formatter (newRoot, 0);
1272
1273 // source is the DOM tree
1274 XercesDOMSupport support;
1275 XercesParserLiaison liaison;
1276 const XercesDOMWrapperParsedSource parsedSource (
1277 Document(), liaison, support,
1278 XalanDOMString (pwszOriginalFilename));
1279
1280 // stylesheet
1281 SettingsConverterStream xsl;
1282
1283 int xrc = xalan.transform (parsedSource, xsl, formatter);
1284
1285 if (xrc)
1286 {
1287 LogFlow(("xalan.transform() = %d (%s)\n", xrc, xalan.getLastError()));
1288
1289 newRoot->release();
1290 rc = VERR_FILE_IO_ERROR;
1291 }
1292 else
1293 {
1294 // release the builder and the old document, if any
1295 if (builder)
1296 {
1297 builder->release();
1298 builder = 0;
1299 root = 0;
1300 }
1301 else if (root)
1302 {
1303 root->release();
1304 root = 0;
1305 }
1306
1307 root = newRoot;
1308
1309 // Xalan 1.9.0 (and 1.10.0) is stupid bacause disregards the
1310 // XSLT specs and ignores the exclude-result-prefixes stylesheet
1311 // attribute, flooding all the elements with stupid xmlns
1312 // specifications. Here we remove them.
1313 XMLCh *xmlnsName = XMLString::transcode ("xmlns");
1314 XMLCh *xmlnsVBox = XMLString::transcode ("http://www.innotek.de/VirtualBox-settings");
1315 DOMNodeIterator *iter =
1316 newRoot->createNodeIterator (newRoot, DOMNodeFilter::SHOW_ELEMENT,
1317 NULL, false);
1318 DOMNode *node = NULL;
1319 while ((node = iter->nextNode()) != NULL)
1320 {
1321 DOMElement *elem = static_cast <DOMElement *> (node);
1322 if (elem->getParentNode() == newRoot)
1323 continue;
1324
1325 const XMLCh *xmlns = elem->getAttribute (xmlnsName);
1326 if (xmlns == NULL)
1327 continue;
1328 if (xmlns[0] == 0 ||
1329 XMLString::compareString (xmlns, xmlnsVBox) == 0)
1330 {
1331 elem->removeAttribute (xmlnsName);
1332 }
1333 }
1334 XMLString::release (&xmlnsVBox);
1335 XMLString::release (&xmlnsName);
1336 }
1337 }
1338 catch (...)
1339 {
1340 rc = VERR_FILE_IO_ERROR;
1341 }
1342
1343 if (VBOX_FAILURE (rc))
1344 {
1345 // this will transfer ownership of the string
1346 if (ppszErrorMessage)
1347 {
1348 if (xalan.getLastError())
1349 *ppszErrorMessage = RTStrDup (xalan.getLastError());
1350 else
1351 *ppszErrorMessage = errorHandler.takeErrorMessage();
1352 }
1353 }
1354
1355 XalanTransformer::terminate();
1356
1357 return rc;
1358}
1359#endif
1360
1361int CfgLoader::Create()
1362{
1363 if (!xmlInitialized)
1364 {
1365 return VERR_NOT_SUPPORTED;
1366 }
1367
1368 int rc = VINF_SUCCESS;
1369
1370 static const XMLCh gLS[] = { chLatin_L, chLatin_S, chNull };
1371 DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(gLS);
1372
1373 if (impl)
1374 {
1375 // creating an empty doc is a non-standard extension to DOM specs.
1376 // we're using it since we're bound to Xerces anyway.
1377 root = impl->createDocument();
1378 }
1379 if (!root)
1380 {
1381 rc = VERR_NOT_SUPPORTED;
1382 }
1383
1384 return rc;
1385}
1386
1387int CfgLoader::FreeConfiguration (CfgLoader *pcfg)
1388{
1389 int rc = VINF_SUCCESS;
1390
1391 while (pcfg->pfirstnode)
1392 {
1393 CfgNode::ReleaseNode (pcfg->pfirstnode);
1394 }
1395
1396 delete pcfg;
1397
1398 return rc;
1399}
1400
1401int CfgLoader::CreateNode (const char *pszName, CfgNode **ppnode)
1402{
1403 return getNode (root, pszName, 0, ppnode, CfgNode::fCreateIfNotExists);
1404}
1405
1406int CfgLoader::GetNode (const char *pszName, unsigned uIndex, CfgNode **ppnode)
1407{
1408 return getNode (root, pszName, uIndex, ppnode, CfgNode::fSearch);
1409}
1410
1411int CfgLoader::getNode (DOMNode *prootnode, const char *pszName, unsigned uIndex, CfgNode **ppnode, unsigned flags)
1412{
1413 int rc = VINF_SUCCESS;
1414
1415 CfgNode *pnode = new CfgNode (this);
1416
1417 if (!pnode)
1418 {
1419 rc = VERR_NO_MEMORY;
1420 }
1421 else if (!prootnode)
1422 {
1423 rc = VERR_NOT_SUPPORTED;
1424 }
1425 else
1426 {
1427 rc = pnode->resolve (prootnode, pszName, uIndex, flags);
1428 }
1429
1430 if (VBOX_SUCCESS(rc))
1431 {
1432 pnode->next = pfirstnode;
1433 if (pfirstnode)
1434 {
1435 pfirstnode->prev = pnode;
1436 }
1437 pfirstnode = pnode;
1438
1439 *ppnode = pnode;
1440 }
1441 else
1442 {
1443 if (pnode)
1444 {
1445 delete pnode;
1446 }
1447 }
1448
1449 return rc;
1450}
1451
1452/* CfgNode implementation */
1453CfgNode::CfgNode (CfgLoader *pcfg)
1454 :
1455 pConfiguration (pcfg),
1456 next (NULL),
1457 prev (NULL)
1458{
1459}
1460
1461CfgNode::~CfgNode ()
1462{
1463}
1464
1465int CfgNode::ReleaseNode (CfgNode *pnode)
1466{
1467 int rc = VINF_SUCCESS;
1468
1469 if (pnode->next)
1470 {
1471 pnode->next->prev = pnode->prev;
1472 }
1473
1474 if (pnode->prev)
1475 {
1476 pnode->prev->next = pnode->next;
1477 }
1478 else
1479 {
1480 pnode->pConfiguration->pfirstnode = pnode->next;
1481 }
1482
1483 delete pnode;
1484
1485 return rc;
1486}
1487
1488int CfgNode::DeleteNode (CfgNode *pnode)
1489{
1490 int rc = VINF_SUCCESS;
1491
1492 DOMNode *pparent = pnode->pdomnode->getParentNode ();
1493
1494 pparent->removeChild (pnode->pdomnode);
1495
1496 pnode->pdomnode = 0;
1497
1498 ReleaseNode (pnode);
1499
1500 return rc;
1501}
1502
1503int CfgNode::resolve (DOMNode *root, const char *pszName, unsigned uIndex, unsigned flags)
1504{
1505 static const char *pcszEmptyName = "";
1506
1507 if (!root)
1508 {
1509 return VERR_PATH_NOT_FOUND;
1510 }
1511
1512 if (!pszName)
1513 {
1514 // special case for resolving any child
1515 pszName = pcszEmptyName;
1516 }
1517
1518 if ((flags & (fCreateIfNotExists | fAppend)) && !(*pszName))
1519 {
1520 return VERR_INVALID_PARAMETER;
1521 }
1522
1523 int rc = VINF_SUCCESS;
1524
1525 PRTUTF16 pwszName = NULL;
1526
1527 rc = RTStrToUtf16 (pszName, &pwszName);
1528
1529 if (VBOX_SUCCESS(rc))
1530 {
1531 XMLCh *puszComponent = pwszName;
1532
1533 bool lastComponent = false;
1534
1535 int lastindex = XMLString::indexOf (puszComponent, '/');
1536
1537 if (lastindex == -1)
1538 {
1539 lastindex = XMLString::stringLen (puszComponent);
1540 lastComponent = true;
1541 }
1542
1543 rc = VERR_PATH_NOT_FOUND;
1544
1545 for (;;)
1546 {
1547 DOMNode *child = 0, *first = 0;
1548
1549 if (!lastComponent || !(flags & fAppend))
1550 {
1551 for (child = root->getFirstChild(); child != 0; child=child->getNextSibling())
1552 {
1553 if (child->getNodeType () == DOMNode::ELEMENT_NODE)
1554 {
1555 if (!lastindex || XMLString::compareNString (child->getNodeName (), puszComponent, lastindex) == 0)
1556 {
1557 if (lastComponent)
1558 {
1559 if (uIndex == 0)
1560 {
1561 pdomnode = child;
1562 rc = VINF_SUCCESS;
1563 break;
1564 }
1565 uIndex--;
1566 continue;
1567 }
1568 else
1569 {
1570 if (!first)
1571 {
1572 first = child;
1573 }
1574 else
1575 {
1576 break;
1577 }
1578 }
1579 }
1580 }
1581 }
1582 }
1583
1584 if (first)
1585 {
1586 if (child)
1587 {
1588 // some element in the path (except the last) has
1589 // siblingswith the same name
1590 rc = VERR_INVALID_PARAMETER;
1591 break;
1592 }
1593 root = child = first;
1594 }
1595
1596 if (!child)
1597 {
1598 if (flags & (fCreateIfNotExists | fAppend))
1599 {
1600 // Extract the component name to a temporary buffer
1601 RTUTF16 uszName[256];
1602
1603 memcpy (uszName, puszComponent, lastindex * sizeof(uszName[0]));
1604 uszName[lastindex] = 0;
1605
1606 try
1607 {
1608 DOMElement *elem = pConfiguration->Document()->createElement(uszName);
1609 root = root->appendChild(elem);
1610 }
1611
1612 catch (...)
1613 {
1614 Log(( "Error creating element [%ls]\n", uszName ));
1615 rc = VERR_CFG_NO_VALUE;
1616 break;
1617 }
1618
1619 if (lastComponent)
1620 {
1621 pdomnode = root;
1622 rc = VINF_SUCCESS;
1623 break;
1624 }
1625 }
1626 else
1627 {
1628 break;
1629 }
1630 }
1631
1632 puszComponent += lastindex;
1633 if (*puszComponent)
1634 {
1635 puszComponent++;
1636 }
1637
1638 if (!*puszComponent)
1639 {
1640 break;
1641 }
1642
1643 lastindex = XMLString::indexOf (puszComponent, '/');
1644
1645 if (lastindex == -1)
1646 {
1647 lastindex = XMLString::stringLen (puszComponent);
1648 lastComponent = true;
1649 }
1650 }
1651
1652 RTUtf16Free (pwszName);
1653 }
1654
1655 return rc;
1656}
1657
1658int CfgNode::GetChild (const char *pszName, unsigned uIndex, CfgNode **ppnode)
1659{
1660 return pConfiguration->getNode (pdomnode, pszName, uIndex, ppnode, fSearch);
1661}
1662
1663int CfgNode::CreateChildNode (const char *pszName, CfgNode **ppnode)
1664{
1665 return pConfiguration->getNode (pdomnode, pszName, 0, ppnode, fCreateIfNotExists);
1666}
1667
1668int CfgNode::AppendChildNode (const char *pszName, CfgNode **ppnode)
1669{
1670 return pConfiguration->getNode (pdomnode, pszName, 0, ppnode, fAppend);
1671}
1672
1673int CfgNode::CountChildren (const char *pszChildName, unsigned *pCount)
1674{
1675 int rc = VINF_SUCCESS;
1676
1677 PRTUTF16 pwszChildName = NULL;
1678
1679 if (pszChildName)
1680 {
1681 rc = RTStrToUtf16 (pszChildName, &pwszChildName);
1682 }
1683
1684 if (VBOX_SUCCESS(rc))
1685 {
1686 DOMNode *child;
1687
1688 unsigned count = 0;
1689
1690 for (child = pdomnode->getFirstChild(); child != 0; child=child->getNextSibling())
1691 {
1692 if (child->getNodeType () == DOMNode::ELEMENT_NODE)
1693 {
1694 if (pwszChildName == NULL)
1695 {
1696 count++;
1697 }
1698 else if (XMLString::compareString (child->getNodeName (), pwszChildName) == 0)
1699 {
1700 count++;
1701 }
1702 }
1703 }
1704
1705 if (pwszChildName)
1706 {
1707 RTUtf16Free (pwszChildName);
1708 }
1709
1710 *pCount = count;
1711 }
1712
1713 return rc;
1714}
1715
1716DOMNode *CfgNode::findChildText (void)
1717{
1718 DOMNode *child = NULL;
1719
1720 for (child = pdomnode->getFirstChild(); child != 0; child=child->getNextSibling())
1721 {
1722 if (child->getNodeType () == DOMNode::TEXT_NODE)
1723 {
1724 break;
1725 }
1726 }
1727
1728 return child;
1729}
1730
1731/**
1732 * Gets the value of the given attribute as a UTF-16 string.
1733 * The returned string is owned by CfgNode, the caller must not free it.
1734 *
1735 * @param pszName Attribute name.
1736 * @param ppwszValue Where to store a pointer to the attribute value.
1737 *
1738 * @return IPRT status code.
1739 */
1740int CfgNode::getValueString (const char *pszName, PRTUTF16 *ppwszValue)
1741{
1742 int rc = VINF_SUCCESS;
1743
1744 PCRTUTF16 pwszValue = NULL;
1745
1746 if (!pszName)
1747 {
1748 DOMNode *ptext = findChildText ();
1749
1750 if (ptext)
1751 {
1752 pwszValue = ptext->getNodeValue ();
1753 }
1754 }
1755 else
1756 {
1757 PRTUTF16 pwszName = NULL;
1758
1759 rc = RTStrToUtf16 (pszName, &pwszName);
1760
1761 if (VBOX_SUCCESS(rc))
1762 {
1763 DOMAttr *attr = (static_cast<DOMElement *>(pdomnode))->getAttributeNode (pwszName);
1764 if (attr)
1765 {
1766 pwszValue = attr->getValue ();
1767 }
1768
1769 RTUtf16Free (pwszName);
1770 }
1771 }
1772
1773 if (!pwszValue)
1774 {
1775 *ppwszValue = NULL;
1776 rc = VERR_CFG_NO_VALUE;
1777 }
1778 else
1779 {
1780 *ppwszValue = (PRTUTF16)pwszValue;
1781 }
1782
1783 return rc;
1784}
1785
1786int CfgNode::setValueString (const char *pszName, PRTUTF16 pwszValue)
1787{
1788 int rc = VINF_SUCCESS;
1789
1790 if (!pszName)
1791 {
1792 DOMText *val = NULL;
1793
1794 try
1795 {
1796 val = pConfiguration->Document ()->createTextNode(pwszValue);
1797 }
1798
1799 catch (...)
1800 {
1801 rc = VERR_CFG_NO_VALUE;
1802 }
1803
1804 if (VBOX_SUCCESS(rc) && val)
1805 {
1806 try
1807 {
1808 DOMNode *poldtext = findChildText ();
1809
1810 if (poldtext)
1811 {
1812 pdomnode->replaceChild (val, poldtext);
1813 poldtext->release();
1814 }
1815 else
1816 {
1817 pdomnode->appendChild (val);
1818 }
1819 }
1820
1821 catch (...)
1822 {
1823 rc = VERR_CFG_NO_VALUE;
1824 val->release();
1825 }
1826 }
1827 }
1828 else
1829 {
1830 PRTUTF16 pwszName = NULL;
1831
1832 rc = RTStrToUtf16 (pszName, &pwszName);
1833
1834 if (VBOX_SUCCESS(rc))
1835 {
1836 try
1837 {
1838 static_cast<DOMElement *>(pdomnode)->setAttribute (pwszName, pwszValue);
1839 }
1840
1841 catch (...)
1842 {
1843 rc = VERR_CFG_NO_VALUE;
1844 }
1845 }
1846 }
1847
1848 return rc;
1849}
1850
1851int CfgNode::QueryUInt32 (const char *pszName, uint32_t *pulValue)
1852{
1853 int rc = VINF_SUCCESS;
1854
1855 PRTUTF16 pwszValue = NULL;
1856
1857 rc = getValueString (pszName, &pwszValue);
1858 if (VBOX_SUCCESS(rc))
1859 {
1860 uint32_t value = 0;
1861 char *pszValue = NULL;
1862
1863 rc = RTUtf16ToUtf8 (pwszValue, &pszValue);
1864 if (VBOX_SUCCESS (rc))
1865 {
1866 rc = RTStrToUInt32Ex (pszValue, NULL, 0, &value);
1867 if (VBOX_SUCCESS(rc))
1868 {
1869 *pulValue = value;
1870 }
1871
1872 RTStrFree (pszValue);
1873 }
1874 }
1875
1876 return rc;
1877}
1878
1879int CfgNode::SetUInt32 (const char *pszName, uint32_t ulValue, unsigned int uiBase)
1880{
1881 int rc = VINF_SUCCESS;
1882
1883 char szValue [64];
1884
1885 rc = RTStrFormatNumber (szValue, (uint64_t) ulValue, uiBase, 0, 0,
1886 RTSTR_F_32BIT | RTSTR_F_SPECIAL);
1887 if (VBOX_SUCCESS (rc))
1888 {
1889 PRTUTF16 pwszValue = NULL;
1890
1891 rc = RTStrToUtf16 (szValue, &pwszValue);
1892 if (VBOX_SUCCESS (rc))
1893 {
1894 rc = setValueString (pszName, pwszValue);
1895 RTUtf16Free (pwszValue);
1896 }
1897 }
1898
1899 return rc;
1900}
1901
1902int CfgNode::QueryUInt64 (const char *pszName, uint64_t *pullValue)
1903{
1904 int rc = VINF_SUCCESS;
1905
1906 PRTUTF16 pwszValue = NULL;
1907
1908 rc = getValueString (pszName, &pwszValue);
1909 if (VBOX_SUCCESS(rc))
1910 {
1911 uint64_t value = 0;
1912 char *pszValue = NULL;
1913
1914 rc = RTUtf16ToUtf8 (pwszValue, &pszValue);
1915 if (VBOX_SUCCESS (rc))
1916 {
1917 rc = RTStrToUInt64Ex (pszValue, NULL, 0, &value);
1918 if (VBOX_SUCCESS(rc))
1919 {
1920 *pullValue = value;
1921 }
1922
1923 RTStrFree (pszValue);
1924 }
1925 }
1926
1927 return rc;
1928}
1929
1930int CfgNode::SetUInt64 (const char *pszName, uint64_t ullValue, unsigned int uiBase)
1931{
1932 int rc = VINF_SUCCESS;
1933
1934 char szValue [64];
1935
1936 rc = RTStrFormatNumber (szValue, ullValue, uiBase, 0, 0,
1937 RTSTR_F_64BIT | RTSTR_F_SPECIAL);
1938 if (VBOX_SUCCESS (rc))
1939 {
1940 PRTUTF16 pwszValue = NULL;
1941
1942 rc = RTStrToUtf16 (szValue, &pwszValue);
1943 if (VBOX_SUCCESS (rc))
1944 {
1945 rc = setValueString (pszName, pwszValue);
1946 RTUtf16Free (pwszValue);
1947 }
1948 }
1949
1950 return rc;
1951}
1952
1953int CfgNode::QueryInt32 (const char *pszName, int32_t *plValue)
1954{
1955 int rc = VINF_SUCCESS;
1956
1957 PRTUTF16 pwszValue = NULL;
1958
1959 rc = getValueString (pszName, &pwszValue);
1960 if (VBOX_SUCCESS(rc))
1961 {
1962 int32_t value = 0;
1963 char *pszValue = NULL;
1964
1965 rc = RTUtf16ToUtf8 (pwszValue, &pszValue);
1966 if (VBOX_SUCCESS (rc))
1967 {
1968 rc = RTStrToInt32Ex (pszValue, NULL, 0, &value);
1969 if (VBOX_SUCCESS(rc))
1970 {
1971 *plValue = value;
1972 }
1973
1974 RTStrFree (pszValue);
1975 }
1976 }
1977
1978 return rc;
1979}
1980
1981int CfgNode::SetInt32 (const char *pszName, int32_t lValue, unsigned int uiBase)
1982{
1983 int rc = VINF_SUCCESS;
1984
1985 char szValue [64];
1986
1987 rc = RTStrFormatNumber (szValue, (uint64_t) lValue, uiBase, 0, 0,
1988 RTSTR_F_32BIT | RTSTR_F_VALSIGNED | RTSTR_F_SPECIAL);
1989 if (VBOX_SUCCESS (rc))
1990 {
1991 PRTUTF16 pwszValue = NULL;
1992
1993 rc = RTStrToUtf16 (szValue, &pwszValue);
1994 if (VBOX_SUCCESS (rc))
1995 {
1996 rc = setValueString (pszName, pwszValue);
1997 RTUtf16Free (pwszValue);
1998 }
1999 }
2000
2001 return rc;
2002}
2003
2004int CfgNode::QueryInt64 (const char *pszName, int64_t *pllValue)
2005{
2006 int rc = VINF_SUCCESS;
2007
2008 PRTUTF16 pwszValue = NULL;
2009
2010 rc = getValueString (pszName, &pwszValue);
2011 if (VBOX_SUCCESS(rc))
2012 {
2013 int64_t value = 0;
2014 char *pszValue = NULL;
2015
2016 rc = RTUtf16ToUtf8 (pwszValue, &pszValue);
2017 if (VBOX_SUCCESS (rc))
2018 {
2019 rc = RTStrToInt64Ex (pszValue, NULL, 0, &value);
2020 if (VBOX_SUCCESS(rc))
2021 {
2022 *pllValue = value;
2023 }
2024
2025 RTStrFree (pszValue);
2026 }
2027 }
2028
2029 return rc;
2030}
2031
2032int CfgNode::SetInt64 (const char *pszName, int64_t llValue, unsigned int uiBase)
2033{
2034 int rc = VINF_SUCCESS;
2035
2036 char szValue [64];
2037
2038 rc = RTStrFormatNumber (szValue, (uint64_t) llValue, uiBase, 0, 0,
2039 RTSTR_F_64BIT | RTSTR_F_VALSIGNED | RTSTR_F_SPECIAL);
2040 if (VBOX_SUCCESS (rc))
2041 {
2042 PRTUTF16 pwszValue = NULL;
2043
2044 rc = RTStrToUtf16 (szValue, &pwszValue);
2045 if (VBOX_SUCCESS (rc))
2046 {
2047 rc = setValueString (pszName, pwszValue);
2048 RTUtf16Free (pwszValue);
2049 }
2050 }
2051
2052 return rc;
2053}
2054
2055int CfgNode::QueryUInt16 (const char *pszName, uint16_t *puhValue)
2056{
2057 int rc = VINF_SUCCESS;
2058
2059 PRTUTF16 pwszValue = NULL;
2060
2061 rc = getValueString (pszName, &pwszValue);
2062 if (VBOX_SUCCESS(rc))
2063 {
2064 uint16_t value = 0;
2065 char *pszValue = NULL;
2066
2067 rc = RTUtf16ToUtf8 (pwszValue, &pszValue);
2068 if (VBOX_SUCCESS (rc))
2069 {
2070 rc = RTStrToUInt16Ex (pszValue, NULL, 0, &value);
2071 if (VBOX_SUCCESS(rc))
2072 {
2073 *puhValue = value;
2074 }
2075
2076 RTStrFree (pszValue);
2077 }
2078 }
2079
2080 return rc;
2081}
2082
2083int CfgNode::SetUInt16 (const char *pszName, uint16_t uhValue, unsigned int uiBase)
2084{
2085 int rc = VINF_SUCCESS;
2086
2087 char szValue [64];
2088
2089 rc = RTStrFormatNumber (szValue, (uint64_t) uhValue, uiBase, 0, 0,
2090 RTSTR_F_16BIT | RTSTR_F_SPECIAL);
2091 if (VBOX_SUCCESS (rc))
2092 {
2093 PRTUTF16 pwszValue = NULL;
2094
2095 rc = RTStrToUtf16 (szValue, &pwszValue);
2096 if (VBOX_SUCCESS (rc))
2097 {
2098 rc = setValueString (pszName, pwszValue);
2099 RTUtf16Free (pwszValue);
2100 }
2101 }
2102
2103 return rc;
2104}
2105
2106int CfgNode::QueryBin (const char *pszName, void *pvValue, unsigned cbValue, unsigned *pcbValue)
2107{
2108 int rc = VINF_SUCCESS;
2109
2110 PRTUTF16 pwszValue = NULL;
2111
2112 rc = getValueString (pszName, &pwszValue);
2113 if (VBOX_SUCCESS(rc))
2114 {
2115 if ( (XMLString::stringLen (pwszValue) / 2) > cbValue)
2116 {
2117 rc = VERR_BUFFER_OVERFLOW;
2118 }
2119 else if (!pvValue)
2120 {
2121 rc = VERR_INVALID_POINTER;
2122 }
2123 else
2124 {
2125 rc = wstr_to_bin (pwszValue, pvValue, cbValue, pcbValue);
2126 }
2127 }
2128
2129 return rc;
2130}
2131
2132int CfgNode::SetBin (const char *pszName, const void *pvValue, unsigned cbValue)
2133{
2134 int rc = VINF_SUCCESS;
2135
2136 PRTUTF16 pwszValue = NULL;
2137
2138 rc = bin_to_wstr (pvValue, cbValue, &pwszValue);
2139 if (VBOX_SUCCESS (rc))
2140 {
2141 rc = setValueString (pszName, pwszValue);
2142 RTUtf16Free (pwszValue);
2143 }
2144
2145 return rc;
2146}
2147
2148int CfgNode::QueryString (const char *pszName, void **pValue, unsigned cbValue, unsigned *pcbValue, bool returnUtf16)
2149{
2150 int rc = VINF_SUCCESS;
2151
2152 PRTUTF16 pwszValue = NULL;
2153
2154 // start with 0 bytes return value
2155 if (pcbValue)
2156 *pcbValue = 0;
2157
2158 rc = getValueString (pszName, &pwszValue);
2159
2160 if (VBOX_SUCCESS(rc))
2161 {
2162 if (returnUtf16)
2163 {
2164 // make a copy
2165 *pValue = (void*)SysAllocString((const OLECHAR*)pwszValue);
2166 } else
2167 {
2168 char *psz = NULL;
2169
2170 rc = RTUtf16ToUtf8 (pwszValue, &psz);
2171
2172 if (VBOX_SUCCESS(rc))
2173 {
2174 unsigned l = strlen (psz) + 1; // take trailing nul to account
2175
2176 *pcbValue = l;
2177
2178 if (l > cbValue)
2179 {
2180 rc = VERR_BUFFER_OVERFLOW;
2181 }
2182 else if (!*pValue)
2183 {
2184 rc = VERR_INVALID_POINTER;
2185 }
2186 else
2187 {
2188 memcpy (*pValue, psz, l);
2189 }
2190
2191 RTStrFree (psz);
2192 }
2193 }
2194 }
2195 return rc;
2196}
2197
2198int CfgNode::SetString (const char *pszName, const char *pszValue, unsigned cbValue, bool isUtf16)
2199{
2200 int rc = VINF_SUCCESS;
2201
2202 PRTUTF16 pwszValue = NULL;
2203
2204 if (isUtf16)
2205 pwszValue = (PRTUTF16)pszValue;
2206 else
2207 rc = RTStrToUtf16 (pszValue, &pwszValue);
2208
2209 if (VBOX_SUCCESS (rc))
2210 {
2211 rc = setValueString (pszName, pwszValue);
2212
2213 if (!isUtf16)
2214 RTUtf16Free (pwszValue);
2215 }
2216
2217 return rc;
2218}
2219
2220int CfgNode::QueryBool (const char *pszName, bool *pfValue)
2221{
2222 int rc = VINF_SUCCESS;
2223
2224 PRTUTF16 pwszValue = NULL;
2225 rc = getValueString (pszName, &pwszValue);
2226 if (VBOX_SUCCESS (rc))
2227 {
2228 char *pszValue = NULL;
2229 rc = RTUtf16ToUtf8 (pwszValue, &pszValue);
2230 if (VBOX_SUCCESS (rc))
2231 {
2232 if ( !stricmp (pszValue, "true")
2233 || !stricmp (pszValue, "yes")
2234 || !stricmp (pszValue, "on"))
2235 *pfValue = true;
2236 else if ( !stricmp (pszValue, "false")
2237 || !stricmp (pszValue, "no")
2238 || !stricmp (pszValue, "off"))
2239 *pfValue = false;
2240 else
2241 rc = VERR_CFG_INVALID_FORMAT;
2242 RTStrFree (pszValue);
2243 }
2244 }
2245
2246 return rc;
2247}
2248
2249int CfgNode::SetBool (const char *pszName, bool fValue)
2250{
2251 return SetString (pszName, fValue ? "true" : "false", fValue ? 4 : 5, false);
2252}
2253
2254int CfgNode::DeleteAttribute (const char *pszName)
2255{
2256 int rc = VINF_SUCCESS;
2257
2258 PRTUTF16 pwszName = NULL;
2259
2260 rc = RTStrToUtf16 (pszName, &pwszName);
2261
2262 if (VBOX_SUCCESS (rc))
2263 {
2264 try
2265 {
2266 (static_cast<DOMElement *>(pdomnode))->removeAttribute (pwszName);
2267 }
2268
2269 catch (...)
2270 {
2271 rc = VERR_CFG_NO_VALUE;
2272 }
2273
2274 RTUtf16Free (pwszName);
2275 }
2276
2277 return rc;
2278}
2279
2280/* Configuration loader public entry points.
2281 */
2282
2283CFGLDRR3DECL(int) CFGLDRCreate(CFGHANDLE *phcfg)
2284{
2285 if (!phcfg)
2286 {
2287 return VERR_INVALID_POINTER;
2288 }
2289
2290 CfgLoader *pcfgldr = new CfgLoader ();
2291
2292 if (!pcfgldr)
2293 {
2294 return VERR_NO_MEMORY;
2295 }
2296
2297 int rc = pcfgldr->Create();
2298
2299 if (VBOX_SUCCESS(rc))
2300 {
2301 *phcfg = pcfgldr;
2302 }
2303 else
2304 {
2305 delete pcfgldr;
2306 }
2307
2308 return rc;
2309}
2310
2311CFGLDRR3DECL(int) CFGLDRLoad (CFGHANDLE *phcfg,
2312 const char *pszFileName, RTFILE hFileHandle,
2313 const char *pszExternalSchemaLocation, bool bDoNamespaces,
2314 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
2315 char **ppszErrorMessage)
2316{
2317 if (!phcfg || !pszFileName)
2318 return VERR_INVALID_POINTER;
2319
2320 CfgLoader *pcfgldr = new CfgLoader();
2321 if (!pcfgldr)
2322 return VERR_NO_MEMORY;
2323
2324 int rc = pcfgldr->Load (pszFileName, hFileHandle,
2325 pszExternalSchemaLocation, bDoNamespaces,
2326 pfnEntityResolver, ppszErrorMessage);
2327
2328 if (VBOX_SUCCESS(rc))
2329 *phcfg = pcfgldr;
2330 else
2331 delete pcfgldr;
2332
2333 return rc;
2334}
2335
2336CFGLDRR3DECL(int) CFGLDRFree(CFGHANDLE hcfg)
2337{
2338 if (!hcfg)
2339 {
2340 return VERR_INVALID_HANDLE;
2341 }
2342
2343 CfgLoader::FreeConfiguration (hcfg);
2344
2345 return VINF_SUCCESS;
2346}
2347
2348CFGLDRR3DECL(int) CFGLDRSave(CFGHANDLE hcfg,
2349 char **ppszErrorMessage)
2350{
2351 if (!hcfg)
2352 {
2353 return VERR_INVALID_HANDLE;
2354 }
2355 return hcfg->Save (NULL, NIL_RTFILE, ppszErrorMessage);
2356}
2357
2358CFGLDRR3DECL(int) CFGLDRSaveAs(CFGHANDLE hcfg,
2359 const char *pszFilename, RTFILE hFileHandle,
2360 char **ppszErrorMessage)
2361{
2362 if (!hcfg)
2363 {
2364 return VERR_INVALID_HANDLE;
2365 }
2366 if (!pszFilename)
2367 {
2368 return VERR_INVALID_POINTER;
2369 }
2370 return hcfg->Save (pszFilename, hFileHandle, ppszErrorMessage);
2371}
2372
2373CFGLDRR3DECL(int) CFGLDRTransform (CFGHANDLE hcfg,
2374 const char *pszTemlateLocation,
2375 PFNCFGLDRENTITYRESOLVER pfnEntityResolver,
2376 char **ppszErrorMessage)
2377{
2378#ifdef VBOX_XML_XSLT
2379 if (!hcfg)
2380 {
2381 return VERR_INVALID_HANDLE;
2382 }
2383 if (!pszTemlateLocation)
2384 {
2385 return VERR_INVALID_POINTER;
2386 }
2387 return hcfg->Transform (pszTemlateLocation, pfnEntityResolver, ppszErrorMessage);
2388#else
2389 return VERR_NOT_SUPPORTED;
2390#endif
2391}
2392
2393CFGLDRR3DECL(int) CFGLDRGetNode(CFGHANDLE hcfg, const char *pszName, unsigned uIndex, CFGNODE *phnode)
2394{
2395 if (!hcfg)
2396 {
2397 return VERR_INVALID_HANDLE;
2398 }
2399 if (!phnode)
2400 {
2401 return VERR_INVALID_POINTER;
2402 }
2403 return hcfg->GetNode (pszName, uIndex, phnode);
2404}
2405
2406CFGLDRR3DECL(int) CFGLDRGetChildNode(CFGNODE hparent, const char *pszName, unsigned uIndex, CFGNODE *phnode)
2407{
2408 if (!hparent)
2409 {
2410 return VERR_INVALID_HANDLE;
2411 }
2412 if (!phnode)
2413 {
2414 return VERR_INVALID_POINTER;
2415 }
2416 return hparent->GetChild (pszName, uIndex, phnode);
2417}
2418
2419CFGLDRR3DECL(int) CFGLDRCreateNode(CFGHANDLE hcfg, const char *pszName, CFGNODE *phnode)
2420{
2421 if (!hcfg)
2422 {
2423 return VERR_INVALID_HANDLE;
2424 }
2425 if (!phnode || !pszName)
2426 {
2427 return VERR_INVALID_POINTER;
2428 }
2429 return hcfg->CreateNode (pszName, phnode);
2430}
2431
2432CFGLDRR3DECL(int) CFGLDRCreateChildNode(CFGNODE hparent, const char *pszName, CFGNODE *phnode)
2433{
2434 if (!hparent)
2435 {
2436 return VERR_INVALID_HANDLE;
2437 }
2438 if (!phnode || !pszName)
2439 {
2440 return VERR_INVALID_POINTER;
2441 }
2442 return hparent->CreateChildNode (pszName, phnode);
2443}
2444
2445CFGLDRR3DECL(int) CFGLDRAppendChildNode(CFGNODE hparent, const char *pszName, CFGNODE *phnode)
2446{
2447 if (!hparent)
2448 {
2449 return VERR_INVALID_HANDLE;
2450 }
2451 if (!phnode || !pszName)
2452 {
2453 return VERR_INVALID_POINTER;
2454 }
2455 return hparent->AppendChildNode (pszName, phnode);
2456}
2457
2458CFGLDRR3DECL(int) CFGLDRReleaseNode(CFGNODE hnode)
2459{
2460 if (!hnode)
2461 {
2462 return VERR_INVALID_HANDLE;
2463 }
2464 return CfgNode::ReleaseNode (hnode);
2465}
2466
2467CFGLDRR3DECL(int) CFGLDRDeleteNode(CFGNODE hnode)
2468{
2469 if (!hnode)
2470 {
2471 return VERR_INVALID_HANDLE;
2472 }
2473 return CfgNode::DeleteNode (hnode);
2474}
2475
2476CFGLDRR3DECL(int) CFGLDRCountChildren(CFGNODE hnode, const char *pszChildName, unsigned *pCount)
2477{
2478 if (!hnode)
2479 {
2480 return VERR_INVALID_HANDLE;
2481 }
2482 if (!pCount)
2483 {
2484 return VERR_INVALID_POINTER;
2485 }
2486 return hnode->CountChildren (pszChildName, pCount);
2487}
2488
2489CFGLDRR3DECL(int) CFGLDRQueryUInt32(CFGNODE hnode, const char *pszName, uint32_t *pulValue)
2490{
2491 if (!hnode)
2492 {
2493 return VERR_INVALID_HANDLE;
2494 }
2495 if (!pulValue)
2496 {
2497 return VERR_INVALID_POINTER;
2498 }
2499 return hnode->QueryUInt32 (pszName, pulValue);
2500}
2501
2502CFGLDRR3DECL(int) CFGLDRSetUInt32(CFGNODE hnode, const char *pszName, uint32_t ulValue)
2503{
2504 if (!hnode)
2505 {
2506 return VERR_INVALID_HANDLE;
2507 }
2508 return hnode->SetUInt32 (pszName, ulValue);
2509}
2510
2511CFGLDRR3DECL(int) CFGLDRSetUInt32Ex(CFGNODE hnode, const char *pszName, uint32_t ulValue, unsigned int uiBase)
2512{
2513 if (!hnode)
2514 {
2515 return VERR_INVALID_HANDLE;
2516 }
2517 return hnode->SetUInt32 (pszName, ulValue, uiBase);
2518}
2519
2520CFGLDRR3DECL(int) CFGLDRQueryUInt64(CFGNODE hnode, const char *pszName, uint64_t *pullValue)
2521{
2522 if (!hnode)
2523 {
2524 return VERR_INVALID_HANDLE;
2525 }
2526 if (!pullValue)
2527 {
2528 return VERR_INVALID_POINTER;
2529 }
2530 return hnode->QueryUInt64 (pszName, pullValue);
2531}
2532
2533CFGLDRR3DECL(int) CFGLDRSetUInt64(CFGNODE hnode, const char *pszName, uint64_t ullValue)
2534{
2535 if (!hnode)
2536 {
2537 return VERR_INVALID_HANDLE;
2538 }
2539 return hnode->SetUInt64 (pszName, ullValue);
2540}
2541
2542CFGLDRR3DECL(int) CFGLDRSetUInt64Ex(CFGNODE hnode, const char *pszName, uint64_t ullValue, unsigned int uiBase)
2543{
2544 if (!hnode)
2545 {
2546 return VERR_INVALID_HANDLE;
2547 }
2548 return hnode->SetUInt64 (pszName, ullValue, uiBase);
2549}
2550
2551CFGLDRR3DECL(int) CFGLDRQueryInt32(CFGNODE hnode, const char *pszName, int32_t *plValue)
2552{
2553 if (!hnode)
2554 {
2555 return VERR_INVALID_HANDLE;
2556 }
2557 return hnode->QueryInt32 (pszName, plValue);
2558}
2559
2560CFGLDRR3DECL(int) CFGLDRSetInt32(CFGNODE hnode, const char *pszName, int32_t lValue)
2561{
2562 if (!hnode)
2563 {
2564 return VERR_INVALID_HANDLE;
2565 }
2566 return hnode->SetInt32 (pszName, lValue);
2567}
2568
2569CFGLDRR3DECL(int) CFGLDRSetInt32Ex(CFGNODE hnode, const char *pszName, int32_t lValue, unsigned int uiBase)
2570{
2571 if (!hnode)
2572 {
2573 return VERR_INVALID_HANDLE;
2574 }
2575 return hnode->SetInt32 (pszName, lValue, uiBase);
2576}
2577
2578CFGLDRR3DECL(int) CFGLDRQueryInt64(CFGNODE hnode, const char *pszName, int64_t *pllValue)
2579{
2580 if (!hnode)
2581 {
2582 return VERR_INVALID_HANDLE;
2583 }
2584 return hnode->QueryInt64 (pszName, pllValue);
2585}
2586
2587CFGLDRR3DECL(int) CFGLDRSetInt64(CFGNODE hnode, const char *pszName, int64_t llValue)
2588{
2589 if (!hnode)
2590 {
2591 return VERR_INVALID_HANDLE;
2592 }
2593 return hnode->SetInt64 (pszName, llValue);
2594}
2595
2596CFGLDRR3DECL(int) CFGLDRSetInt64Ex(CFGNODE hnode, const char *pszName, int64_t llValue, unsigned int uiBase)
2597{
2598 if (!hnode)
2599 {
2600 return VERR_INVALID_HANDLE;
2601 }
2602 return hnode->SetInt64 (pszName, llValue, uiBase);
2603}
2604
2605CFGLDRR3DECL(int) CFGLDRQueryUInt16(CFGNODE hnode, const char *pszName, uint16_t *puhValue)
2606{
2607 if (!hnode)
2608 {
2609 return VERR_INVALID_HANDLE;
2610 }
2611 if (!puhValue)
2612 {
2613 return VERR_INVALID_POINTER;
2614 }
2615 return hnode->QueryUInt16 (pszName, puhValue);
2616}
2617
2618CFGLDRR3DECL(int) CFGLDRSetUInt16(CFGNODE hnode, const char *pszName, uint16_t uhValue)
2619{
2620 if (!hnode)
2621 {
2622 return VERR_INVALID_HANDLE;
2623 }
2624 return hnode->SetUInt16 (pszName, uhValue);
2625}
2626
2627CFGLDRR3DECL(int) CFGLDRSetUInt16Ex(CFGNODE hnode, const char *pszName, uint16_t uhValue, unsigned int uiBase)
2628{
2629 if (!hnode)
2630 {
2631 return VERR_INVALID_HANDLE;
2632 }
2633 return hnode->SetUInt16 (pszName, uhValue, uiBase);
2634}
2635
2636CFGLDRR3DECL(int) CFGLDRQueryBin(CFGNODE hnode, const char *pszName, void *pvValue, unsigned cbValue, unsigned *pcbValue)
2637{
2638 if (!hnode)
2639 {
2640 return VERR_INVALID_HANDLE;
2641 }
2642 if (!pcbValue)
2643 {
2644 return VERR_INVALID_POINTER;
2645 }
2646 return hnode->QueryBin (pszName, pvValue, cbValue, pcbValue);
2647}
2648
2649CFGLDRR3DECL(int) CFGLDRSetBin(CFGNODE hnode, const char *pszName, void *pvValue, unsigned cbValue)
2650{
2651 if (!hnode)
2652 {
2653 return VERR_INVALID_HANDLE;
2654 }
2655 if (!pvValue)
2656 {
2657 return VERR_INVALID_POINTER;
2658 }
2659 return hnode->SetBin (pszName, pvValue, cbValue);
2660}
2661
2662CFGLDRR3DECL(int) CFGLDRQueryString(CFGNODE hnode, const char *pszName, char *pszValue, unsigned cbValue, unsigned *pcbValue)
2663{
2664 if (!hnode)
2665 {
2666 return VERR_INVALID_HANDLE;
2667 }
2668 if (!pcbValue)
2669 {
2670 return VERR_INVALID_POINTER;
2671 }
2672 return hnode->QueryString (pszName, (void**)&pszValue, cbValue, pcbValue, false);
2673}
2674
2675CFGLDRR3DECL(int) CFGLDRQueryBSTR(CFGNODE hnode, const char *pszName, BSTR *ppwszValue)
2676{
2677 if (!hnode)
2678 {
2679 return VERR_INVALID_HANDLE;
2680 }
2681 if (!ppwszValue)
2682 {
2683 return VERR_INVALID_POINTER;
2684 }
2685 return hnode->QueryString(pszName, (void**)ppwszValue, 0, NULL, true);
2686}
2687
2688CFGLDRR3DECL(int) CFGLDRQueryUUID(CFGNODE hnode, const char *pszName, PRTUUID pUUID)
2689{
2690 if (!hnode)
2691 {
2692 return VERR_INVALID_HANDLE;
2693 }
2694 if (!pUUID)
2695 {
2696 return VERR_INVALID_POINTER;
2697 }
2698
2699 // we need it as UTF8
2700 unsigned size;
2701 int rc;
2702 rc = CFGLDRQueryString(hnode, pszName, NULL, 0, &size);
2703 if (rc == VERR_BUFFER_OVERFLOW)
2704 {
2705 char *uuidUtf8 = new char[size];
2706 rc = CFGLDRQueryString(hnode, pszName, uuidUtf8, size, &size);
2707 if (VBOX_SUCCESS(rc))
2708 {
2709 // remove the curly brackets
2710 uuidUtf8[strlen(uuidUtf8) - 1] = '\0';
2711 rc = RTUuidFromStr(pUUID, &uuidUtf8[1]);
2712 }
2713 delete[] uuidUtf8;
2714 }
2715 return rc;
2716}
2717
2718CFGLDRR3DECL(int) CFGLDRSetUUID(CFGNODE hnode, const char *pszName, PCRTUUID pUuid)
2719{
2720 if (!hnode)
2721 {
2722 return VERR_INVALID_HANDLE;
2723 }
2724 if (!pUuid)
2725 {
2726 return VERR_INVALID_HANDLE;
2727 }
2728
2729 // UUID + curly brackets
2730 char strUuid[RTUUID_STR_LENGTH + 2 * sizeof(char)];
2731 strUuid[0] = '{';
2732 RTUuidToStr((const PRTUUID)pUuid, &strUuid[1], RTUUID_STR_LENGTH);
2733 strcat(strUuid, "}");
2734 return hnode->SetString (pszName, strUuid, strlen (strUuid), false);
2735}
2736
2737CFGLDRR3DECL(int) CFGLDRSetString(CFGNODE hnode, const char *pszName, const char *pszValue)
2738{
2739 if (!hnode)
2740 {
2741 return VERR_INVALID_HANDLE;
2742 }
2743 if (!pszValue)
2744 {
2745 return VERR_INVALID_POINTER;
2746 }
2747 return hnode->SetString (pszName, pszValue, strlen (pszValue), false);
2748}
2749
2750CFGLDRR3DECL(int) CFGLDRSetBSTR(CFGNODE hnode, const char *pszName, const BSTR bstrValue)
2751{
2752 if (!hnode)
2753 {
2754 return VERR_INVALID_HANDLE;
2755 }
2756 if (!bstrValue)
2757 {
2758 return VERR_INVALID_POINTER;
2759 }
2760 return hnode->SetString (pszName, (char*)bstrValue, RTUtf16Len((PCRTUTF16)bstrValue), true);
2761}
2762
2763CFGLDRR3DECL(int) CFGLDRQueryBool(CFGNODE hnode, const char *pszName, bool *pfValue)
2764{
2765 if (!hnode)
2766 {
2767 return VERR_INVALID_HANDLE;
2768 }
2769 if (!pfValue)
2770 {
2771 return VERR_INVALID_POINTER;
2772 }
2773
2774 return hnode->QueryBool (pszName, pfValue);
2775}
2776
2777CFGLDRR3DECL(int) CFGLDRSetBool(CFGNODE hnode, const char *pszName, bool fValue)
2778{
2779 if (!hnode)
2780 {
2781 return VERR_INVALID_HANDLE;
2782 }
2783
2784 return hnode->SetBool (pszName, fValue);
2785}
2786
2787CFGLDRR3DECL(int) CFGLDRQueryDateTime(CFGNODE hnode, const char *pszName, int64_t *pi64Value)
2788{
2789 if (!hnode)
2790 {
2791 return VERR_INVALID_HANDLE;
2792 }
2793 if (!pi64Value)
2794 {
2795 return VERR_INVALID_POINTER;
2796 }
2797
2798 /* query as UTF8 string */
2799 unsigned size = 0;
2800 int rc = CFGLDRQueryString(hnode, pszName, NULL, 0, &size);
2801 if (rc != VERR_BUFFER_OVERFLOW)
2802 return rc;
2803
2804 char *pszValue = new char[size];
2805 char *pszBuf = new char[size];
2806 rc = CFGLDRQueryString(hnode, pszName, pszValue, size, &size);
2807 if (VBOX_SUCCESS(rc)) do
2808 {
2809 /* Parse xsd:dateTime. The format is:
2810 * '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
2811 * where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z' */
2812 uint32_t yyyy = 0;
2813 uint16_t mm = 0, dd = 0, hh = 0, mi = 0, ss = 0;
2814 if (sscanf(pszValue, "%d-%hu-%huT%hu:%hu:%hu%s",
2815 &yyyy, &mm, &dd, &hh, &mi, &ss, pszBuf) != 7)
2816 {
2817 rc = VERR_PARSE_ERROR;
2818 break;
2819 }
2820
2821 /* currently, we accept only the UTC timezone ('Z'),
2822 * ignoring fractional seconds, if present */
2823 if (pszBuf[0] == 'Z' ||
2824 (pszBuf[0] == '.' && pszBuf[strlen(pszBuf)-1] == 'Z'))
2825 {
2826 /* start with an error */
2827 rc = VERR_PARSE_ERROR;
2828
2829 RTTIME time = { yyyy, (uint8_t) mm, 0, 0, (uint8_t) dd,
2830 (uint8_t) hh, (uint8_t) mi, (uint8_t) ss, 0,
2831 RTTIME_FLAGS_TYPE_UTC, 0 };
2832 if (RTTimeNormalize (&time))
2833 {
2834 RTTIMESPEC timeSpec;
2835 if (RTTimeImplode (&timeSpec, &time))
2836 {
2837 *pi64Value = RTTimeSpecGetMilli (&timeSpec);
2838 rc = VINF_SUCCESS;
2839 }
2840 }
2841 }
2842 else
2843 rc = VERR_PARSE_ERROR;
2844 }
2845 while (0);
2846
2847 delete[] pszBuf;
2848 delete[] pszValue;
2849
2850 return rc;
2851}
2852
2853CFGLDRR3DECL(int) CFGLDRSetDateTime(CFGNODE hnode, const char *pszName, int64_t i64Value)
2854{
2855 if (!hnode)
2856 {
2857 return VERR_INVALID_HANDLE;
2858 }
2859
2860 RTTIMESPEC timeSpec;
2861 RTTimeSpecSetMilli (&timeSpec, i64Value);
2862 RTTIME time;
2863 if (!RTTimeExplode (&time, &timeSpec))
2864 return VERR_PARSE_ERROR;
2865
2866 /* Store xsd:dateTime. The format is:
2867 * '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
2868 * where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z' */
2869 char aszBuf [256];
2870 RTStrPrintf(aszBuf, sizeof(aszBuf),
2871 "%04ld-%02hd-%02hdT%02hd:%02hd:%02hdZ",
2872 time.i32Year, (uint16_t) time.u8Month, (uint16_t) time.u8MonthDay,
2873 (uint16_t) time.u8Hour, (uint16_t) time.u8Minute, (uint16_t) time.u8Second);
2874
2875 return hnode->SetString (pszName, aszBuf, strlen (aszBuf), false);
2876}
2877
2878CFGLDRR3DECL(int) CFGLDRDeleteAttribute(CFGNODE hnode, const char *pszName)
2879{
2880 if (!hnode)
2881 {
2882 return VERR_INVALID_HANDLE;
2883 }
2884 if (!pszName)
2885 {
2886 return VERR_INVALID_POINTER;
2887 }
2888 return hnode->DeleteAttribute (pszName);
2889}
2890
2891CFGLDRR3DECL(int) CFGLDRInitialize (void)
2892{
2893 int rc = VINF_SUCCESS;
2894
2895 if (!initXML ())
2896 {
2897 rc = VERR_NOT_SUPPORTED;
2898 }
2899
2900 return rc;
2901}
2902
2903CFGLDRR3DECL(void) CFGLDRShutdown (void)
2904{
2905 /// @todo delete CfgLoaders
2906 terminateXML ();
2907}
2908
2909#ifdef STANDALONE_TEST
2910
2911#include <iprt/runtime.h>
2912
2913int main(int argc, char **argv)
2914{
2915 Log(("Configuration loader standalone test\n"));
2916
2917 CFGHANDLE hcfg = 0;
2918 CFGNODE hnode = 0;
2919 unsigned count = 0;
2920 unsigned i;
2921 char *cfgFilename = "vboxcfg.xml";
2922 char *cfgFilenameSaved = "testas.xml";
2923
2924 /*
2925 * Initialize the VBox runtime without loading
2926 * the kernel support driver
2927 */
2928 int rc = RTR3Init(false);
2929 if (VBOX_FAILURE(rc))
2930 {
2931 Log(("RTInit failed %d\n", rc));
2932 goto l_exit_0;
2933 }
2934
2935 rc = CFGLDRInitialize();
2936 if (VBOX_FAILURE(rc))
2937 {
2938 Log(("Initialize failed %d\n", rc));
2939 goto l_exit_0;
2940 }
2941
2942 rc = CFGLDRLoad(&hcfg, cfgFilename, "vboxcfg.xsd");
2943 if (VBOX_FAILURE(rc))
2944 {
2945 Log(("Load failed %d\n", rc));
2946 goto l_exit_0;
2947 }
2948
2949 printf("Configuration loaded from %s\n", cfgFilename);
2950
2951 rc = CFGLDRCreateNode(hcfg, "Configuration/Something/DeviceManager/DeviceList", &hnode);
2952 if (VBOX_FAILURE(rc))
2953 {
2954 Log(("CreateNode failed %d\n", rc));
2955 goto l_exit_1;
2956 }
2957 rc = CFGLDRSetString(hnode, "GUID", "testtestte");
2958 rc = CFGLDRReleaseNode(hnode);
2959
2960 rc = CFGLDRGetNode(hcfg, "Configuration/Managers/DeviceManager/DeviceList", 0, &hnode);
2961 if (VBOX_FAILURE(rc))
2962 {
2963 Log(("GetNode failed %d\n", rc));
2964 goto l_exit_1;
2965 }
2966
2967 rc = CFGLDRCountChildren(hnode, "Device", &count);
2968 if (VBOX_FAILURE(rc))
2969 {
2970 Log(("CountChildren failed %d\n", rc));
2971 goto l_exit_2;
2972 }
2973
2974 Log(("Number of child nodes %d\n", count));
2975
2976 for (i = 0; i < count; i++)
2977 {
2978 CFGNODE hchild = 0;
2979 unsigned cbValue;
2980 char szValue[256];
2981
2982 rc = CFGLDRGetChildNode(hnode, "Device", i, &hchild);
2983 if (VBOX_FAILURE(rc))
2984 {
2985 Log(("GetChildNode failed %d\n", rc));
2986 goto l_exit_2;
2987 }
2988
2989 unsigned dummy;
2990 rc = CFGLDRCountChildren(hchild, NULL, &dummy);
2991 Log(("Number of child nodes of Device %d\n", dummy));
2992
2993 int32_t value;
2994
2995 rc = CFGLDRQueryInt32(hchild, "int", &value);
2996 Log(("Child node %d (rc = %d): int = %d\n", i, rc, value));
2997
2998 rc = CFGLDRQueryString(hchild, NULL, szValue, sizeof (szValue), &cbValue);
2999 if (VBOX_FAILURE(rc))
3000 {
3001 Log(("QueryString failed %d\n", rc));
3002 }
3003 Log(("Child node %d: (length = %d) = '%s'\n", i, cbValue, szValue));
3004
3005 rc = CFGLDRSetString(hchild, "GUID", "testtesttest");
3006 if (VBOX_FAILURE(rc))
3007 {
3008 Log(("SetString failed %d\n", rc));
3009 }
3010
3011 rc = CFGLDRDeleteAttribute(hchild, "int");
3012 Log(("Attribute delete %d (rc = %d)\n", i, rc));
3013
3014 CFGLDRSetBin(hchild, "Bin", (void *)CFGLDRSetBin, 100);
3015 CFGLDRSetInt32(hchild, "int32", 1973);
3016// CFGLDRSetUInt64(hchild, "uint64", 0x1973);
3017
3018 CFGNODE hnew = 0;
3019 CFGLDRCreateChildNode(hchild, "testnode", &hnew);
3020 rc = CFGLDRSetString(hchild, NULL, "her");
3021 if (VBOX_FAILURE(rc))
3022 {
3023 Log(("_SetString failed %d\n", rc));
3024 }
3025 rc = CFGLDRSetString(hnew, NULL, "neher");
3026 if (VBOX_FAILURE(rc))
3027 {
3028 Log(("+SetString failed %d\n", rc));
3029 }
3030 CFGLDRReleaseNode(hchild);
3031 }
3032
3033 rc = CFGLDRSaveAs(hcfg, cfgFilenameSaved);
3034 if (VBOX_FAILURE(rc))
3035 {
3036 Log(("SaveAs failed %d\n", rc));
3037 goto l_exit_2;
3038 }
3039
3040 Log(("Configuration saved as %s\n", cfgFilenameSaved));
3041
3042l_exit_2:
3043
3044 rc = CFGLDRReleaseNode(hnode);
3045 if (VBOX_FAILURE(rc))
3046 {
3047 Log(("ReleaseNode failed %d\n", rc));
3048 }
3049
3050l_exit_1:
3051
3052 rc = CFGLDRFree(hcfg);
3053 if (VBOX_FAILURE(rc))
3054 {
3055 Log(("Load failed %d\n", rc));
3056 }
3057
3058l_exit_0:
3059
3060 CFGLDRShutdown();
3061
3062 Log(("Test completed."));
3063 return rc;
3064}
3065#endif
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