VirtualBox

source: vbox/trunk/src/VBox/Main/xml/Settings.cpp@ 21945

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

Main: coding style: have Main obey the standard VirtualBox coding style rules (no functional changes)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 33.9 KB
Line 
1/** @file
2 * Settings File Manipulation API.
3 */
4
5/*
6 * Copyright (C) 2007 Sun Microsystems, Inc.
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
17 * Clara, CA 95054 USA or visit http://www.sun.com if you need
18 * additional information or have any questions.
19 */
20
21#include <iprt/err.h>
22#include <iprt/file.h>
23#include <iprt/lock.h>
24
25#include <libxml/tree.h>
26#include <libxml/parser.h>
27#include <libxml/globals.h>
28#include <libxml/xmlIO.h>
29#include <libxml/xmlsave.h>
30#include <libxml/uri.h>
31
32#include <libxml/xmlschemas.h>
33
34#include <libxslt/xsltInternals.h>
35#include <libxslt/transform.h>
36#include <libxslt/xsltutils.h>
37
38#include <list>
39
40// #include <string.h>
41
42#include "VBox/settings.h"
43
44#include "Logging.h"
45
46namespace settings
47{
48
49// Helpers
50////////////////////////////////////////////////////////////////////////////////
51
52inline int sFromHex (char aChar)
53{
54 if (aChar >= '0' && aChar <= '9')
55 return aChar - '0';
56 if (aChar >= 'A' && aChar <= 'F')
57 return aChar - 'A' + 0xA;
58 if (aChar >= 'a' && aChar <= 'f')
59 return aChar - 'a' + 0xA;
60
61 throw ENoConversion(com::Utf8StrFmt("'%c' (0x%02X) is not hex", aChar, aChar));
62}
63
64inline char sToHex (int aDigit)
65{
66 return (aDigit < 0xA) ? aDigit + '0' : aDigit - 0xA + 'A';
67}
68
69static char *duplicate_chars (const char *that)
70{
71 char *result = NULL;
72 if (that != NULL)
73 {
74 size_t len = strlen (that) + 1;
75 result = new char [len];
76 if (result != NULL)
77 memcpy (result, that, len);
78 }
79 return result;
80}
81
82//////////////////////////////////////////////////////////////////////////////
83// string -> type conversions
84//////////////////////////////////////////////////////////////////////////////
85
86uint64_t FromStringInteger (const char *aValue, bool aSigned,
87 int aBits, uint64_t aMin, uint64_t aMax)
88{
89 if (aValue == NULL)
90 throw ENoValue();
91
92 switch (aBits)
93 {
94 case 8:
95 case 16:
96 case 32:
97 case 64:
98 break;
99 default:
100 throw xml::ENotImplemented (RT_SRC_POS);
101 }
102
103 if (aSigned)
104 {
105 int64_t result;
106 int vrc = RTStrToInt64Full (aValue, 0, &result);
107 if (RT_SUCCESS(vrc))
108 {
109 if (result >= (int64_t) aMin && result <= (int64_t) aMax)
110 return (uint64_t) result;
111 }
112 }
113 else
114 {
115 uint64_t result;
116 int vrc = RTStrToUInt64Full (aValue, 0, &result);
117 if (RT_SUCCESS(vrc))
118 {
119 if (result >= aMin && result <= aMax)
120 return result;
121 }
122 }
123
124 throw ENoConversion(com::Utf8StrFmt("'%s' is not integer", aValue));
125}
126
127template<> bool FromString <bool> (const char *aValue)
128{
129 if (aValue == NULL)
130 throw ENoValue();
131
132 if (strcmp (aValue, "true") == 0 ||
133 strcmp (aValue, "1") == 0)
134 /* This contradicts the XML Schema's interpretation of boolean: */
135 //strcmp (aValue, "yes") == 0 ||
136 //strcmp (aValue, "on") == 0)
137 return true;
138 else if (strcmp (aValue, "false") == 0 ||
139 strcmp (aValue, "0") == 0)
140 /* This contradicts the XML Schema's interpretation of boolean: */
141 //strcmp (aValue, "no") == 0 ||
142 //strcmp (aValue, "off") == 0)
143 return false;
144
145 throw ENoConversion(com::Utf8StrFmt("'%s' is not bool", aValue));
146}
147
148template<> RTTIMESPEC FromString <RTTIMESPEC> (const char *aValue)
149{
150 if (aValue == NULL)
151 throw ENoValue();
152
153 /* Parse ISO date (xsd:dateTime). The format is:
154 * '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
155 * where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z' */
156 uint32_t yyyy = 0;
157 uint16_t mm = 0, dd = 0, hh = 0, mi = 0, ss = 0;
158 char buf [256];
159 if (strlen (aValue) > RT_ELEMENTS (buf) - 1 ||
160 sscanf (aValue, "%d-%hu-%huT%hu:%hu:%hu%s",
161 &yyyy, &mm, &dd, &hh, &mi, &ss, buf) == 7)
162 {
163 /* currently, we accept only the UTC timezone ('Z'),
164 * ignoring fractional seconds, if present */
165 if (buf [0] == 'Z' ||
166 (buf [0] == '.' && buf [strlen (buf) - 1] == 'Z'))
167 {
168 RTTIME time = { yyyy, (uint8_t) mm, 0, 0, (uint8_t) dd,
169 (uint8_t) hh, (uint8_t) mi, (uint8_t) ss, 0,
170 RTTIME_FLAGS_TYPE_UTC };
171 if (RTTimeNormalize (&time))
172 {
173 RTTIMESPEC timeSpec;
174 if (RTTimeImplode (&timeSpec, &time))
175 return timeSpec;
176 }
177 }
178 else
179 throw ENoConversion(com::Utf8StrFmt("'%s' is not UTC date", aValue));
180 }
181
182 throw ENoConversion(com::Utf8StrFmt("'%s' is not ISO date", aValue));
183}
184
185stdx::char_auto_ptr FromString (const char *aValue, size_t *aLen)
186{
187 if (aValue == NULL)
188 throw ENoValue();
189
190 /* each two chars produce one byte */
191 size_t len = strlen (aValue) / 2;
192
193 /* therefore, the original length must be even */
194 if (len % 2 != 0)
195 throw ENoConversion(com::Utf8StrFmt("'%.*s' is not binary data",
196 aLen, aValue));
197
198 stdx::char_auto_ptr result (new char [len]);
199
200 const char *src = aValue;
201 char *dst = result.get();
202
203 for (size_t i = 0; i < len; ++ i, ++ dst)
204 {
205 *dst = sFromHex (*src ++) << 4;
206 *dst |= sFromHex (*src ++);
207 }
208
209 if (aLen != NULL)
210 *aLen = len;
211
212 return result;
213}
214
215//////////////////////////////////////////////////////////////////////////////
216// type -> string conversions
217//////////////////////////////////////////////////////////////////////////////
218
219stdx::char_auto_ptr ToStringInteger (uint64_t aValue, unsigned int aBase,
220 bool aSigned, int aBits)
221{
222 unsigned int flags = RTSTR_F_SPECIAL;
223 if (aSigned)
224 flags |= RTSTR_F_VALSIGNED;
225
226 /* maximum is binary representation + terminator */
227 size_t len = aBits + 1;
228
229 switch (aBits)
230 {
231 case 8:
232 flags |= RTSTR_F_8BIT;
233 break;
234 case 16:
235 flags |= RTSTR_F_16BIT;
236 break;
237 case 32:
238 flags |= RTSTR_F_32BIT;
239 break;
240 case 64:
241 flags |= RTSTR_F_64BIT;
242 break;
243 default:
244 throw xml::ENotImplemented (RT_SRC_POS);
245 }
246
247 stdx::char_auto_ptr result (new char [len]);
248 if (aBase == 0)
249 aBase = 10;
250 int vrc = RTStrFormatNumber (result.get(), aValue, aBase, 0, 0, flags);
251 if (RT_SUCCESS(vrc))
252 return result;
253
254 throw xml::EIPRTFailure (vrc);
255}
256
257template<> stdx::char_auto_ptr ToString <bool> (const bool &aValue,
258 unsigned int aExtra /* = 0 */)
259{
260 /* Convert to the canonical form according to XML Schema */
261 stdx::char_auto_ptr result (duplicate_chars (aValue ? "true" : "false"));
262 return result;
263}
264
265template<> stdx::char_auto_ptr ToString <RTTIMESPEC> (const RTTIMESPEC &aValue,
266 unsigned int aExtra /* = 0 */)
267{
268 RTTIME time;
269 if (!RTTimeExplode (&time, &aValue))
270 throw ENoConversion(com::Utf8StrFmt("timespec %lld ms is invalid",
271 RTTimeSpecGetMilli (&aValue)));
272
273 /* Store ISO date (xsd:dateTime). The format is:
274 * '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
275 * where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z' */
276 char buf [256];
277 RTStrPrintf (buf, sizeof (buf),
278 "%04ld-%02hd-%02hdT%02hd:%02hd:%02hdZ",
279 time.i32Year, (uint16_t) time.u8Month, (uint16_t) time.u8MonthDay,
280 (uint16_t) time.u8Hour, (uint16_t) time.u8Minute, (uint16_t) time.u8Second);
281
282 stdx::char_auto_ptr result (duplicate_chars (buf));
283 return result;
284}
285
286stdx::char_auto_ptr ToString (const char *aData, size_t aLen)
287{
288 /* each byte will produce two hex digits and there will be a null
289 * terminator */
290 stdx::char_auto_ptr result (new char [aLen * 2 + 1]);
291
292 const char *src = aData;
293 char *dst = result.get();
294
295 for (size_t i = 0; i < aLen; ++ i, ++ src)
296 {
297 *dst++ = sToHex ((*src) >> 4);
298 *dst++ = sToHex ((*src) & 0xF);
299 }
300
301 *dst = '\0';
302
303 return result;
304}
305
306//////////////////////////////////////////////////////////////////////////////
307// XmlKeyBackend Class
308//////////////////////////////////////////////////////////////////////////////
309
310class XmlKeyBackend : public Key::Backend
311{
312public:
313
314 XmlKeyBackend (xmlNodePtr aNode);
315 ~XmlKeyBackend();
316
317 const char *name() const;
318 void setName (const char *aName);
319 const char *value (const char *aName) const;
320 void setValue (const char *aName, const char *aValue);
321
322 Key::List keys (const char *aName = NULL) const;
323 Key findKey (const char *aName) const;
324
325 Key appendKey (const char *aName);
326 void zap();
327
328 void *position() const { return mNode; }
329
330private:
331
332 xmlNodePtr mNode;
333
334 xmlChar *mNodeText;
335
336 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (XmlKeyBackend);
337
338 friend class XmlTreeBackend;
339};
340
341XmlKeyBackend::XmlKeyBackend (xmlNodePtr aNode)
342 : mNode (aNode), mNodeText (NULL)
343{
344 AssertReturnVoid (mNode);
345 AssertReturnVoid (mNode->type == XML_ELEMENT_NODE);
346}
347
348XmlKeyBackend::~XmlKeyBackend()
349{
350 xmlFree (mNodeText);
351}
352
353const char *XmlKeyBackend::name() const
354{
355 return mNode ? (char *) mNode->name : NULL;
356}
357
358void XmlKeyBackend::setName (const char *aName)
359{
360 throw xml::ENotImplemented (RT_SRC_POS);
361}
362
363const char *XmlKeyBackend::value (const char *aName) const
364{
365 if (!mNode)
366 return NULL;
367
368 if (aName == NULL)
369 {
370 /* @todo xmlNodeListGetString (,,1) returns NULL for things like
371 * <Foo></Foo> and may want to return "" in this case to distinguish
372 * from <Foo/> (where NULL is pretty much expected). */
373 if (!mNodeText)
374 unconst(mNodeText) =
375 xmlNodeListGetString (mNode->doc, mNode->children, 0);
376 return (char *) mNodeText;
377 }
378
379 xmlAttrPtr attr = xmlHasProp (mNode, (const xmlChar *) aName);
380 if (!attr)
381 return NULL;
382
383 if (attr->type == XML_ATTRIBUTE_NODE)
384 {
385 /* @todo for now, we only understand the most common case: only 1 text
386 * node comprises the attribute's contents. Otherwise we'd need to
387 * return a newly allocated string buffer to the caller that
388 * concatenates all text nodes and obey him to free it or provide our
389 * own internal map of attribute=value pairs and return const pointers
390 * to values from this map. */
391 if (attr->children != NULL &&
392 attr->children->next == NULL &&
393 (attr->children->type == XML_TEXT_NODE ||
394 attr->children->type == XML_CDATA_SECTION_NODE))
395 return (char *) attr->children->content;
396 }
397 else if (attr->type == XML_ATTRIBUTE_DECL)
398 {
399 return (char *) ((xmlAttributePtr) attr)->defaultValue;
400 }
401
402 return NULL;
403}
404
405void XmlKeyBackend::setValue (const char *aName, const char *aValue)
406{
407 if (!mNode)
408 return;
409
410 if (aName == NULL)
411 {
412 xmlChar *value = (xmlChar *) aValue;
413 if (value != NULL)
414 {
415 value = xmlEncodeSpecialChars (mNode->doc, value);
416 if (value == NULL)
417 throw std::bad_alloc();
418 }
419
420 xmlNodeSetContent (mNode, value);
421
422 if (value != (xmlChar *) aValue)
423 xmlFree (value);
424
425 /* outdate the node text holder */
426 if (mNodeText != NULL)
427 {
428 xmlFree (mNodeText);
429 mNodeText = NULL;
430 }
431
432 return;
433 }
434
435 if (aValue == NULL)
436 {
437 /* remove the attribute if it exists */
438 xmlAttrPtr attr = xmlHasProp (mNode, (const xmlChar *) aName);
439 if (attr != NULL)
440 {
441 int rc = xmlRemoveProp (attr);
442 if (rc != 0)
443 throw xml::EInvalidArg (RT_SRC_POS);
444 }
445 return;
446 }
447
448 xmlAttrPtr attr = xmlSetProp (mNode, (const xmlChar *) aName,
449 (const xmlChar *) aValue);
450 if (attr == NULL)
451 throw std::bad_alloc();
452}
453
454Key::List XmlKeyBackend::keys (const char *aName /* = NULL */) const
455{
456 Key::List list;
457
458 if (!mNode)
459 return list;
460
461 for (xmlNodePtr node = mNode->children; node; node = node->next)
462 {
463 if (node->type == XML_ELEMENT_NODE)
464 {
465 if (aName == NULL ||
466 strcmp (aName, (char *) node->name) == 0)
467 list.push_back (Key (new XmlKeyBackend (node)));
468 }
469 }
470
471 return list;
472}
473
474Key XmlKeyBackend::findKey (const char *aName) const
475{
476 Key key;
477
478 if (!mNode)
479 return key;
480
481 for (xmlNodePtr node = mNode->children; node; node = node->next)
482 {
483 if (node->type == XML_ELEMENT_NODE)
484 {
485 if (aName == NULL ||
486 strcmp (aName, (char *) node->name) == 0)
487 {
488 key = Key (new XmlKeyBackend (node));
489 break;
490 }
491 }
492 }
493
494 return key;
495}
496
497Key XmlKeyBackend::appendKey (const char *aName)
498{
499 if (!mNode)
500 return Key();
501
502 xmlNodePtr node = xmlNewChild (mNode, NULL, (const xmlChar *) aName, NULL);
503 if (node == NULL)
504 throw std::bad_alloc();
505
506 return Key (new XmlKeyBackend (node));
507}
508
509void XmlKeyBackend::zap()
510{
511 if (!mNode)
512 return;
513
514 xmlUnlinkNode (mNode);
515 xmlFreeNode (mNode);
516 mNode = NULL;
517}
518
519//////////////////////////////////////////////////////////////////////////////
520// XmlTreeBackend Class
521//////////////////////////////////////////////////////////////////////////////
522
523struct XmlTreeBackend::Data
524{
525 Data() : ctxt (NULL), doc (NULL)
526 , inputResolver (NULL)
527 , autoConverter (NULL), oldVersion (NULL) {}
528
529 xmlParserCtxtPtr ctxt;
530 xmlDocPtr doc;
531
532 Key root;
533
534 InputResolver *inputResolver;
535
536 AutoConverter *autoConverter;
537 char *oldVersion;
538
539 std::auto_ptr <stdx::exception_trap_base> trappedErr;
540
541 /**
542 * This is to avoid throwing exceptions while in libxml2 code and
543 * redirect them to our level instead. Also used to perform clean up
544 * by deleting the I/O stream instance and self when requested.
545 */
546 struct IOCtxt
547 {
548 IOCtxt (xml::Stream *aStream, std::auto_ptr <stdx::exception_trap_base> &aErr)
549 : stream (aStream), deleteStreamOnClose (false)
550 , err (aErr) {}
551
552 template <typename T>
553 void setErr (const T& aErr) { err.reset (new stdx::exception_trap <T> (aErr)); }
554
555 void resetErr() { err.reset(); }
556
557 xml::Stream *stream;
558 bool deleteStreamOnClose;
559
560 std::auto_ptr <stdx::exception_trap_base> &err;
561 };
562
563 struct InputCtxt : public IOCtxt
564 {
565 InputCtxt (xml::Input *aInput, std::auto_ptr <stdx::exception_trap_base> &aErr)
566 : IOCtxt (aInput, aErr), input (aInput) {}
567
568 xml::Input *input;
569 };
570
571 struct OutputCtxt : public IOCtxt
572 {
573 OutputCtxt (xml::Output *aOutput, std::auto_ptr <stdx::exception_trap_base> &aErr)
574 : IOCtxt (aOutput, aErr), output (aOutput) {}
575
576 xml::Output *output;
577 };
578};
579
580XmlTreeBackend::XmlTreeBackend()
581 : m (new Data())
582{
583 /* create a parser context */
584 m->ctxt = xmlNewParserCtxt();
585 if (m->ctxt == NULL)
586 throw std::bad_alloc();
587}
588
589XmlTreeBackend::~XmlTreeBackend()
590{
591 reset();
592
593 xmlFreeParserCtxt (m->ctxt);
594 m->ctxt = NULL;
595}
596
597void XmlTreeBackend::setInputResolver (InputResolver &aResolver)
598{
599 m->inputResolver = &aResolver;
600}
601
602void XmlTreeBackend::resetInputResolver()
603{
604 m->inputResolver = NULL;
605}
606
607void XmlTreeBackend::setAutoConverter (AutoConverter &aConverter)
608{
609 m->autoConverter = &aConverter;
610}
611
612void XmlTreeBackend::resetAutoConverter()
613{
614 m->autoConverter = NULL;
615}
616
617const char *XmlTreeBackend::oldVersion() const
618{
619 return m->oldVersion;
620}
621
622extern "C" xmlGenericErrorFunc xsltGenericError;
623extern "C" void *xsltGenericErrorContext;
624
625void XmlTreeBackend::rawRead (xml::Input &aInput, const char *aSchema /* = NULL */,
626 int aFlags /* = 0 */)
627{
628 /* Reset error variables used to memorize exceptions while inside the
629 * libxml2 code. */
630 m->trappedErr.reset();
631
632 /* We use the global lock for the whole duration of this method to serialize
633 * access to thread-unsafe xmlGetExternalEntityLoader() and some other
634 * calls. It means that only one thread is able to parse an XML stream at a
635 * time but another choice would be to patch libxml2/libxslt which is
636 * unwanted now for several reasons. Search for "thread-safe" to find all
637 * unsafe cases. */
638 xml::GlobalLock global;
639 global.setExternalEntityLoader(ExternalEntityLoader);
640
641 sThat = this;
642 xmlDocPtr doc = NULL;
643
644 try
645 {
646 /* Note: when parsing we use XML_PARSE_NOBLANKS to instruct libxml2 to
647 * remove text nodes that contain only blanks. This is important because
648 * otherwise xmlSaveDoc() won't be able to do proper indentation on
649 * output. */
650 /* parse the stream */
651 /* NOTE: new InputCtxt instance will be deleted when the stream is closed by
652 * the libxml2 API (e.g. when calling xmlFreeParserCtxt()) */
653 doc = xmlCtxtReadIO (m->ctxt,
654 ReadCallback, CloseCallback,
655 new Data::InputCtxt (&aInput, m->trappedErr),
656 aInput.uri(), NULL,
657 XML_PARSE_NOBLANKS);
658 if (doc == NULL)
659 {
660 /* look if there was a forwared exception from the lower level */
661 if (m->trappedErr.get() != NULL)
662 m->trappedErr->rethrow();
663
664 throw xml::XmlError(xmlCtxtGetLastError (m->ctxt));
665 }
666
667 char *oldVersion = NULL;
668
669 /* perform automatic document transformation if necessary */
670 if (m->autoConverter != NULL &&
671 m->autoConverter->
672 needsConversion (Key (new XmlKeyBackend (xmlDocGetRootElement (doc))),
673 &oldVersion))
674 {
675 xmlDocPtr xsltDoc = NULL;
676 xsltStylesheetPtr xslt = NULL;
677 char *errorStr = NULL;
678
679 xmlGenericErrorFunc oldXsltGenericError = xsltGenericError;
680 void *oldXsltGenericErrorContext = xsltGenericErrorContext;
681
682 try
683 {
684 /* parse the XSLT template */
685 {
686 xml::Input *xsltInput =
687 m->inputResolver->resolveEntity
688 (m->autoConverter->templateUri(), NULL);
689 /* NOTE: new InputCtxt instance will be deleted when the
690 * stream is closed by the libxml2 API */
691 xsltDoc = xmlCtxtReadIO (m->ctxt,
692 ReadCallback, CloseCallback,
693 new Data::InputCtxt (xsltInput, m->trappedErr),
694 m->autoConverter->templateUri(),
695 NULL, 0);
696 delete xsltInput;
697 }
698
699 if (xsltDoc == NULL)
700 {
701 /* look if there was a forwared exception from the lower level */
702 if (m->trappedErr.get() != NULL)
703 m->trappedErr->rethrow();
704
705 throw xml::XmlError(xmlCtxtGetLastError (m->ctxt));
706 }
707
708 /* setup stylesheet compilation and transformation error
709 * reporting. Note that we could create a new transform context
710 * for doing xsltApplyStylesheetUser and use
711 * xsltSetTransformErrorFunc() on it to set a dedicated error
712 * handler but as long as we already do several non-thread-safe
713 * hacks, this is not really important. */
714
715 xsltGenericError = ValidityErrorCallback;
716 xsltGenericErrorContext = &errorStr;
717
718 xslt = xsltParseStylesheetDoc (xsltDoc);
719 if (xslt == NULL)
720 {
721 if (errorStr != NULL)
722 throw xml::LogicError (errorStr);
723 /* errorStr is freed in catch(...) below */
724
725 throw xml::LogicError (RT_SRC_POS);
726 }
727
728 /* repeat transformations until autoConverter is satisfied */
729 do
730 {
731 xmlDocPtr newDoc = xsltApplyStylesheet (xslt, doc, NULL);
732 if (newDoc == NULL && errorStr == NULL)
733 throw xml::LogicError (RT_SRC_POS);
734
735 if (errorStr != NULL)
736 {
737 xmlFreeDoc (newDoc);
738 throw xml::RuntimeError(errorStr);
739 /* errorStr is freed in catch(...) below */
740 }
741
742 /* replace the old document on success */
743 xmlFreeDoc (doc);
744 doc = newDoc;
745 }
746 while (m->autoConverter->
747 needsConversion (Key (new XmlKeyBackend (xmlDocGetRootElement (doc))),
748 NULL));
749
750 RTStrFree (errorStr);
751
752 /* NOTE: xsltFreeStylesheet() also fress the document
753 * passed to xsltParseStylesheetDoc(). */
754 xsltFreeStylesheet (xslt);
755
756 /* restore the previous generic error func */
757 xsltGenericError = oldXsltGenericError;
758 xsltGenericErrorContext = oldXsltGenericErrorContext;
759 }
760 catch (...)
761 {
762 RTStrFree (errorStr);
763
764 /* NOTE: xsltFreeStylesheet() also fress the document
765 * passed to xsltParseStylesheetDoc(). */
766 if (xslt != NULL)
767 xsltFreeStylesheet (xslt);
768 else if (xsltDoc != NULL)
769 xmlFreeDoc (xsltDoc);
770
771 /* restore the previous generic error func */
772 xsltGenericError = oldXsltGenericError;
773 xsltGenericErrorContext = oldXsltGenericErrorContext;
774
775 RTStrFree (oldVersion);
776
777 throw;
778 }
779 }
780
781 /* validate the document */
782 if (aSchema != NULL)
783 {
784 xmlSchemaParserCtxtPtr schemaCtxt = NULL;
785 xmlSchemaPtr schema = NULL;
786 xmlSchemaValidCtxtPtr validCtxt = NULL;
787 char *errorStr = NULL;
788
789 try
790 {
791 bool valid = false;
792
793 schemaCtxt = xmlSchemaNewParserCtxt (aSchema);
794 if (schemaCtxt == NULL)
795 throw xml::LogicError (RT_SRC_POS);
796
797 /* set our error handlers */
798 xmlSchemaSetParserErrors (schemaCtxt, ValidityErrorCallback,
799 ValidityWarningCallback, &errorStr);
800 xmlSchemaSetParserStructuredErrors (schemaCtxt,
801 StructuredErrorCallback,
802 &errorStr);
803 /* load schema */
804 schema = xmlSchemaParse (schemaCtxt);
805 if (schema != NULL)
806 {
807 validCtxt = xmlSchemaNewValidCtxt (schema);
808 if (validCtxt == NULL)
809 throw xml::LogicError (RT_SRC_POS);
810
811 /* instruct to create default attribute's values in the document */
812 if (aFlags & Read_AddDefaults)
813 xmlSchemaSetValidOptions (validCtxt, XML_SCHEMA_VAL_VC_I_CREATE);
814
815 /* set our error handlers */
816 xmlSchemaSetValidErrors (validCtxt, ValidityErrorCallback,
817 ValidityWarningCallback, &errorStr);
818
819 /* finally, validate */
820 valid = xmlSchemaValidateDoc (validCtxt, doc) == 0;
821 }
822
823 if (!valid)
824 {
825 /* look if there was a forwared exception from the lower level */
826 if (m->trappedErr.get() != NULL)
827 m->trappedErr->rethrow();
828
829 if (errorStr == NULL)
830 throw xml::LogicError (RT_SRC_POS);
831
832 throw xml::RuntimeError(errorStr);
833 /* errorStr is freed in catch(...) below */
834 }
835
836 RTStrFree (errorStr);
837
838 xmlSchemaFreeValidCtxt (validCtxt);
839 xmlSchemaFree (schema);
840 xmlSchemaFreeParserCtxt (schemaCtxt);
841 }
842 catch (...)
843 {
844 RTStrFree (errorStr);
845
846 if (validCtxt)
847 xmlSchemaFreeValidCtxt (validCtxt);
848 if (schema)
849 xmlSchemaFree (schema);
850 if (schemaCtxt)
851 xmlSchemaFreeParserCtxt (schemaCtxt);
852
853 RTStrFree (oldVersion);
854
855 throw;
856 }
857 }
858
859 /* reset the previous tree on success */
860 reset();
861
862 m->doc = doc;
863 /* assign the root key */
864 m->root = Key (new XmlKeyBackend (xmlDocGetRootElement (m->doc)));
865
866 /* memorize the old version string also used as a flag that
867 * the conversion has been performed (transfers ownership) */
868 m->oldVersion = oldVersion;
869
870 sThat = NULL;
871 }
872 catch (...)
873 {
874 if (doc != NULL)
875 xmlFreeDoc (doc);
876
877 sThat = NULL;
878
879 throw;
880 }
881}
882
883void XmlTreeBackend::rawWrite (xml::Output &aOutput)
884{
885 /* reset error variables used to memorize exceptions while inside the
886 * libxml2 code */
887 m->trappedErr.reset();
888
889 /* set up an input stream for parsing the document. This will be deleted
890 * when the stream is closed by the libxml2 API (e.g. when calling
891 * xmlFreeParserCtxt()). */
892 Data::OutputCtxt *outputCtxt =
893 new Data::OutputCtxt (&aOutput, m->trappedErr);
894
895 /* serialize to the stream */
896
897 xmlIndentTreeOutput = 1;
898 xmlTreeIndentString = " ";
899 xmlSaveNoEmptyTags = 0;
900
901 xmlSaveCtxtPtr saveCtxt = xmlSaveToIO (WriteCallback, CloseCallback,
902 outputCtxt, NULL,
903 XML_SAVE_FORMAT);
904 if (saveCtxt == NULL)
905 throw xml::LogicError (RT_SRC_POS);
906
907 long rc = xmlSaveDoc (saveCtxt, m->doc);
908 if (rc == -1)
909 {
910 /* look if there was a forwared exception from the lower level */
911 if (m->trappedErr.get() != NULL)
912 m->trappedErr->rethrow();
913
914 /* there must be an exception from the Output implementation,
915 * otherwise the save operation must always succeed. */
916 throw xml::LogicError (RT_SRC_POS);
917 }
918
919 xmlSaveClose (saveCtxt);
920}
921
922void XmlTreeBackend::reset()
923{
924 RTStrFree (m->oldVersion);
925 m->oldVersion = NULL;
926
927 if (m->doc)
928 {
929 /* reset the root key's node */
930 GetKeyBackend (m->root)->mNode = NULL;
931 /* free the document*/
932 xmlFreeDoc (m->doc);
933 m->doc = NULL;
934 }
935}
936
937Key &XmlTreeBackend::rootKey() const
938{
939 return m->root;
940}
941
942/* static */
943int XmlTreeBackend::ReadCallback (void *aCtxt, char *aBuf, int aLen)
944{
945 AssertReturn(aCtxt != NULL, 0);
946
947 Data::InputCtxt *ctxt = static_cast <Data::InputCtxt *> (aCtxt);
948
949 /* To prevent throwing exceptions while inside libxml2 code, we catch
950 * them and forward to our level using a couple of variables. */
951 try
952 {
953 return ctxt->input->read (aBuf, aLen);
954 }
955 catch (const xml::EIPRTFailure &err) { ctxt->setErr (err); }
956 catch (const xml::Error &err) { ctxt->setErr (err); }
957 catch (const std::exception &err) { ctxt->setErr (err); }
958 catch (...) { ctxt->setErr (xml::LogicError (RT_SRC_POS)); }
959
960 return -1 /* failure */;
961}
962
963/* static */
964int XmlTreeBackend::WriteCallback (void *aCtxt, const char *aBuf, int aLen)
965{
966 AssertReturn(aCtxt != NULL, 0);
967
968 Data::OutputCtxt *ctxt = static_cast <Data::OutputCtxt *> (aCtxt);
969
970 /* To prevent throwing exceptions while inside libxml2 code, we catch
971 * them and forward to our level using a couple of variables. */
972 try
973 {
974 return ctxt->output->write (aBuf, aLen);
975 }
976 catch (const xml::EIPRTFailure &err) { ctxt->setErr (err); }
977 catch (const xml::Error &err) { ctxt->setErr (err); }
978 catch (const std::exception &err) { ctxt->setErr (err); }
979 catch (...) { ctxt->setErr (xml::LogicError (RT_SRC_POS)); }
980
981 return -1 /* failure */;
982}
983
984/* static */
985int XmlTreeBackend::CloseCallback (void *aCtxt)
986{
987 AssertReturn(aCtxt != NULL, 0);
988
989 Data::IOCtxt *ctxt = static_cast <Data::IOCtxt *> (aCtxt);
990
991 /* To prevent throwing exceptions while inside libxml2 code, we catch
992 * them and forward to our level using a couple of variables. */
993 try
994 {
995 /// @todo there is no explicit close semantics in Stream yet
996#if 0
997 ctxt->stream->close();
998#endif
999
1000 /* perform cleanup when necessary */
1001 if (ctxt->deleteStreamOnClose)
1002 delete ctxt->stream;
1003
1004 delete ctxt;
1005
1006 return 0 /* success */;
1007 }
1008 catch (const xml::EIPRTFailure &err) { ctxt->setErr (err); }
1009 catch (const xml::Error &err) { ctxt->setErr (err); }
1010 catch (const std::exception &err) { ctxt->setErr (err); }
1011 catch (...) { ctxt->setErr (xml::LogicError (RT_SRC_POS)); }
1012
1013 return -1 /* failure */;
1014}
1015
1016/* static */
1017void XmlTreeBackend::ValidityErrorCallback (void *aCtxt, const char *aMsg, ...)
1018{
1019 AssertReturnVoid (aCtxt != NULL);
1020 AssertReturnVoid (aMsg != NULL);
1021
1022 char * &str = *(char * *) aCtxt;
1023
1024 char *newMsg = NULL;
1025 {
1026 va_list args;
1027 va_start (args, aMsg);
1028 RTStrAPrintfV (&newMsg, aMsg, args);
1029 va_end (args);
1030 }
1031
1032 AssertReturnVoid (newMsg != NULL);
1033
1034 /* strip spaces, trailing EOLs and dot-like char */
1035 size_t newMsgLen = strlen (newMsg);
1036 while (newMsgLen && strchr (" \n.?!", newMsg [newMsgLen - 1]))
1037 -- newMsgLen;
1038
1039 /* anything left? */
1040 if (newMsgLen > 0)
1041 {
1042 if (str == NULL)
1043 {
1044 str = newMsg;
1045 newMsg [newMsgLen] = '\0';
1046 }
1047 else
1048 {
1049 /* append to the existing string */
1050 size_t strLen = strlen (str);
1051 char *newStr = (char *) RTMemRealloc (str, strLen + 2 + newMsgLen + 1);
1052 AssertReturnVoid (newStr != NULL);
1053
1054 memcpy (newStr + strLen, ".\n", 2);
1055 memcpy (newStr + strLen + 2, newMsg, newMsgLen);
1056 newStr [strLen + 2 + newMsgLen] = '\0';
1057 str = newStr;
1058 RTStrFree (newMsg);
1059 }
1060 }
1061}
1062
1063/* static */
1064void XmlTreeBackend::ValidityWarningCallback (void *aCtxt, const char *aMsg, ...)
1065{
1066 NOREF (aCtxt);
1067 NOREF (aMsg);
1068}
1069
1070/* static */
1071void XmlTreeBackend::StructuredErrorCallback (void *aCtxt, xmlErrorPtr aErr)
1072{
1073 AssertReturnVoid (aCtxt != NULL);
1074 AssertReturnVoid (aErr != NULL);
1075
1076 char * &str = *(char * *) aCtxt;
1077
1078 char *newMsg = xml::XmlError::Format (aErr);
1079 AssertReturnVoid (newMsg != NULL);
1080
1081 if (str == NULL)
1082 str = newMsg;
1083 else
1084 {
1085 /* append to the existing string */
1086 size_t newMsgLen = strlen (newMsg);
1087 size_t strLen = strlen (str);
1088 char *newStr = (char *) RTMemRealloc (str, strLen + newMsgLen + 2);
1089 AssertReturnVoid (newStr != NULL);
1090
1091 memcpy (newStr + strLen, ".\n", 2);
1092 memcpy (newStr + strLen + 2, newMsg, newMsgLen);
1093 str = newStr;
1094 RTStrFree (newMsg);
1095 }
1096}
1097
1098/* static */
1099XmlTreeBackend *XmlTreeBackend::sThat = NULL;
1100
1101/* static */
1102xmlParserInputPtr XmlTreeBackend::ExternalEntityLoader (const char *aURI,
1103 const char *aID,
1104 xmlParserCtxtPtr aCtxt)
1105{
1106 AssertReturn(sThat != NULL, NULL);
1107
1108 if (sThat->m->inputResolver == NULL)
1109 return xml::GlobalLock::callDefaultLoader(aURI, aID, aCtxt);
1110
1111 /* To prevent throwing exceptions while inside libxml2 code, we catch
1112 * them and forward to our level using a couple of variables. */
1113 try
1114 {
1115 xml::Input *input = sThat->m->inputResolver->resolveEntity (aURI, aID);
1116 if (input == NULL)
1117 return NULL;
1118
1119 Data::InputCtxt *ctxt = new Data::InputCtxt (input, sThat->m->trappedErr);
1120 ctxt->deleteStreamOnClose = true;
1121
1122 /* create an input buffer with custom hooks */
1123 xmlParserInputBufferPtr bufPtr =
1124 xmlParserInputBufferCreateIO (ReadCallback, CloseCallback,
1125 ctxt, XML_CHAR_ENCODING_NONE);
1126 if (bufPtr)
1127 {
1128 /* create an input stream */
1129 xmlParserInputPtr inputPtr =
1130 xmlNewIOInputStream (aCtxt, bufPtr, XML_CHAR_ENCODING_NONE);
1131
1132 if (inputPtr != NULL)
1133 {
1134 /* pass over the URI to the stream struct (it's NULL by
1135 * default) */
1136 inputPtr->filename =
1137 (char *) xmlCanonicPath ((const xmlChar *) input->uri());
1138 return inputPtr;
1139 }
1140 }
1141
1142 /* either of libxml calls failed */
1143
1144 if (bufPtr)
1145 xmlFreeParserInputBuffer (bufPtr);
1146
1147 delete input;
1148 delete ctxt;
1149
1150 throw std::bad_alloc();
1151 }
1152 catch (const xml::EIPRTFailure &err) { sThat->m->trappedErr.reset (stdx::new_exception_trap (err)); }
1153 catch (const xml::Error &err) { sThat->m->trappedErr.reset (stdx::new_exception_trap (err)); }
1154 catch (const std::exception &err) { sThat->m->trappedErr.reset (stdx::new_exception_trap (err)); }
1155 catch (...) { sThat->m->trappedErr.reset (stdx::new_exception_trap (xml::LogicError (RT_SRC_POS))); }
1156
1157 return NULL;
1158}
1159
1160
1161} /* namespace settings */
1162
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