Settings.cpp revision 7f1db520ded2b0454dd839fdf9ecae555b3a28fe
/** @file
* Settings File Manipulation API.
*/
/*
* Copyright (C) 2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
#include "VBox/settings.h"
#include "Logging.h"
#include <libxml/xmlschemas.h>
#include <libxslt/xsltInternals.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>
#include <string.h>
namespace settings
{
// Helpers
////////////////////////////////////////////////////////////////////////////////
{
return aChar - '0';
}
{
}
static char *duplicate_chars (const char *that)
{
{
}
return result;
}
//////////////////////////////////////////////////////////////////////////////
// string -> type conversions
//////////////////////////////////////////////////////////////////////////////
{
throw ENoValue();
switch (aBits)
{
case 8:
case 16:
case 32:
case 64:
break;
default:
}
if (aSigned)
{
if (RT_SUCCESS (vrc))
{
}
}
else
{
if (RT_SUCCESS (vrc))
{
return result;
}
}
}
{
throw ENoValue();
/* This contradicts the XML Schema's interpretation of boolean: */
//strcmp (aValue, "yes") == 0 ||
//strcmp (aValue, "on") == 0)
return true;
/* This contradicts the XML Schema's interpretation of boolean: */
//strcmp (aValue, "no") == 0 ||
//strcmp (aValue, "off") == 0)
return false;
}
{
throw ENoValue();
/* Parse ISO date (xsd:dateTime). The format is:
* '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
* where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z' */
char buf [256];
{
/* currently, we accept only the UTC timezone ('Z'),
* ignoring fractional seconds, if present */
if (buf [0] == 'Z' ||
{
if (RTTimeNormalize (&time))
{
return timeSpec;
}
}
else
}
}
{
throw ENoValue();
/* each two chars produce one byte */
/* therefore, the original length must be even */
if (len % 2 != 0)
{
}
return result;
}
//////////////////////////////////////////////////////////////////////////////
// type -> string conversions
//////////////////////////////////////////////////////////////////////////////
{
unsigned int flags = RTSTR_F_SPECIAL;
if (aSigned)
/* maximum is binary representation + terminator */
switch (aBits)
{
case 8:
flags |= RTSTR_F_8BIT;
break;
case 16:
flags |= RTSTR_F_16BIT;
break;
case 32:
flags |= RTSTR_F_32BIT;
break;
case 64:
flags |= RTSTR_F_64BIT;
break;
default:
}
if (RT_SUCCESS (vrc))
return result;
}
unsigned int aExtra /* = 0 */)
{
/* Convert to the canonical form according to XML Schema */
return result;
}
unsigned int aExtra /* = 0 */)
{
RTTimeSpecGetMilli (&aValue)));
/* Store ISO date (xsd:dateTime). The format is:
* '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
* where zzzzzz is: (('+' | '-') hh ':' mm) | 'Z' */
char buf [256];
"%04ld-%02hd-%02hdT%02hd:%02hd:%02hdZ",
return result;
}
{
/* each byte will produce two hex digits and there will be a null
* terminator */
{
}
*dst = '\0';
return result;
}
//////////////////////////////////////////////////////////////////////////////
// XmlKeyBackend Class
//////////////////////////////////////////////////////////////////////////////
{
public:
~XmlKeyBackend();
const char *name() const;
void zap();
private:
friend class XmlTreeBackend;
};
{
}
{
}
const char *XmlKeyBackend::name() const
{
}
{
}
{
if (!mNode)
return NULL;
{
/* @todo xmlNodeListGetString (,,1) returns NULL for things like
* <Foo></Foo> and may want to return "" in this case to distinguish
* from <Foo/> (where NULL is pretty much expected). */
if (!mNodeText)
return (char *) mNodeText;
}
if (!attr)
return NULL;
{
/* @todo for now, we only understand the most common case: only 1 text
* node comprises the attribute's contents. Otherwise we'd need to
* return a newly allocated string buffer to the caller that
* concatenates all text nodes and obey him to free it or provide our
* own internal map of attribute=value pairs and return const pointers
* to values from this map. */
}
{
}
return NULL;
}
{
if (!mNode)
return;
{
{
}
/* outdate the node text holder */
{
}
return;
}
{
/* remove the attribute if it exists */
{
if (rc != 0)
}
return;
}
}
{
if (!mNode)
return list;
{
{
}
}
return list;
}
{
if (!mNode)
return key;
{
{
{
break;
}
}
}
return key;
}
{
if (!mNode)
return Key();
}
void XmlKeyBackend::zap()
{
if (!mNode)
return;
xmlFreeNode (mNode);
}
//////////////////////////////////////////////////////////////////////////////
// XmlTreeBackend Class
//////////////////////////////////////////////////////////////////////////////
{
public:
{
if (!aErr)
}
/**
* Composes a single message for the given error. The caller must free the
* returned string using RTStrFree() when no more necessary.
*/
{
/* strip spaces, trailing EOLs and dot-like char */
-- msgLen;
return finalMsg;
}
};
struct XmlTreeBackend::Data
{
, inputResolver (NULL)
char *oldVersion;
/**
* This is to avoid throwing exceptions while in libxml2 code and
* redirect them to our level instead. Also used to perform clean up
* by deleting the I/O stream instance and self when requested.
*/
struct IOCtxt
{
template <typename T>
bool deleteStreamOnClose;
};
{
};
struct OutputCtxt : public IOCtxt
{
};
};
: m (new Data())
{
/* create a parser context */
m->ctxt = xmlNewParserCtxt();
}
{
reset();
xmlFreeParserCtxt (m->ctxt);
}
{
m->inputResolver = &aResolver;
}
void XmlTreeBackend::resetInputResolver()
{
m->inputResolver = NULL;
}
{
m->autoConverter = &aConverter;
}
void XmlTreeBackend::resetAutoConverter()
{
m->autoConverter = NULL;
}
const char *XmlTreeBackend::oldVersion() const
{
return m->oldVersion;
}
extern "C" void *xsltGenericErrorContext;
int aFlags /* = 0 */)
{
/* Reset error variables used to memorize exceptions while inside the
* libxml2 code. */
m->trappedErr.reset();
/* We use the global lock for the whole duration of this method to serialize
* access to thread-unsafe xmlGetExternalEntityLoader() and some other
* calls. It means that only one thread is able to parse an XML stream at a
* unwanted now for several reasons. Search for "thread-safe" to find all
* unsafe cases. */
sThat = this;
try
{
/* Note: when parsing we use XML_PARSE_NOBLANKS to instruct libxml2 to
* remove text nodes that contain only blanks. This is important because
* otherwise xmlSaveDoc() won't be able to do proper indentation on
* output. */
/* parse the stream */
/* NOTE: new InputCtxt instance will be deleted when the stream is closed by
* the libxml2 API (e.g. when calling xmlFreeParserCtxt()) */
{
/* look if there was a forwared exception from the lower level */
m->trappedErr->rethrow();
}
char *oldVersion = NULL;
/* perform automatic document transformation if necessary */
if (m->autoConverter != NULL &&
m->autoConverter->
&oldVersion))
{
try
{
/* parse the XSLT template */
{
/* NOTE: new InputCtxt instance will be deleted when the
* stream is closed by the libxml2 API */
m->autoConverter->templateUri(),
NULL, 0);
delete xsltInput;
}
{
/* look if there was a forwared exception from the lower level */
m->trappedErr->rethrow();
}
/* setup stylesheet compilation and transformation error
* reporting. Note that we could create a new transform context
* for doing xsltApplyStylesheetUser and use
* xsltSetTransformErrorFunc() on it to set a dedicated error
* handler but as long as we already do several non-thread-safe
* hacks, this is not really important. */
{
/* errorStr is freed in catch(...) below */
}
/* repeat transformations until autoConverter is satisfied */
do
{
{
xmlFreeDoc (newDoc);
/* errorStr is freed in catch(...) below */
}
/* replace the old document on success */
xmlFreeDoc (doc);
}
while (m->autoConverter->
NULL));
/* NOTE: xsltFreeStylesheet() also fress the document
* passed to xsltParseStylesheetDoc(). */
/* restore the previous generic error func */
}
catch (...)
{
/* NOTE: xsltFreeStylesheet() also fress the document
* passed to xsltParseStylesheetDoc(). */
/* restore the previous generic error func */
throw;
}
}
/* validate the document */
{
try
{
bool valid = false;
if (schemaCtxt == NULL)
/* set our error handlers */
&errorStr);
/* load schema */
{
/* instruct to create default attribute's values in the document */
if (aFlags & Read_AddDefaults)
/* set our error handlers */
/* finally, validate */
}
if (!valid)
{
/* look if there was a forwared exception from the lower level */
m->trappedErr->rethrow();
/* errorStr is freed in catch(...) below */
}
}
catch (...)
{
if (validCtxt)
if (schema)
if (schemaCtxt)
throw;
}
}
/* reset the previous tree on success */
reset();
/* assign the root key */
/* memorize the old version string also used as a flag that
* the conversion has been performed (transfers ownership) */
m->oldVersion = oldVersion;
}
catch (...)
{
xmlFreeDoc (doc);
throw;
}
}
{
/* reset error variables used to memorize exceptions while inside the
* libxml2 code */
m->trappedErr.reset();
/* set up an input stream for parsing the document. This will be deleted
* when the stream is closed by the libxml2 API (e.g. when calling
* xmlFreeParserCtxt()). */
/* serialize to the stream */
xmlIndentTreeOutput = 1;
xmlTreeIndentString = " ";
xmlSaveNoEmptyTags = 0;
if (rc == -1)
{
/* look if there was a forwared exception from the lower level */
m->trappedErr->rethrow();
/* there must be an exception from the Output implementation,
* otherwise the save operation must always succeed. */
}
}
void XmlTreeBackend::reset()
{
RTStrFree (m->oldVersion);
m->oldVersion = NULL;
if (m->doc)
{
/* reset the root key's node */
/* free the document*/
xmlFreeDoc (m->doc);
}
}
{
return m->root;
}
/* static */
{
/* To prevent throwing exceptions while inside libxml2 code, we catch
* them and forward to our level using a couple of variables. */
try
{
}
return -1 /* failure */;
}
/* static */
{
/* To prevent throwing exceptions while inside libxml2 code, we catch
* them and forward to our level using a couple of variables. */
try
{
}
return -1 /* failure */;
}
/* static */
{
/* To prevent throwing exceptions while inside libxml2 code, we catch
* them and forward to our level using a couple of variables. */
try
{
/// @todo there is no explicit close semantics in Stream yet
#if 0
#endif
/* perform cleanup when necessary */
if (ctxt->deleteStreamOnClose)
delete ctxt;
return 0 /* success */;
}
return -1 /* failure */;
}
/* static */
{
{
}
/* strip spaces, trailing EOLs and dot-like char */
-- newMsgLen;
/* anything left? */
if (newMsgLen > 0)
{
{
}
else
{
/* append to the existing string */
}
}
}
/* static */
{
}
/* static */
{
else
{
/* append to the existing string */
}
}
/* static */
/* static */
const char *aID,
{
/* To prevent throwing exceptions while inside libxml2 code, we catch
* them and forward to our level using a couple of variables. */
try
{
return NULL;
ctxt->deleteStreamOnClose = true;
/* create an input buffer with custom hooks */
if (bufPtr)
{
/* create an input stream */
{
/* pass over the URI to the stream struct (it's NULL by
* default) */
return inputPtr;
}
}
/* either of libxml calls failed */
if (bufPtr)
delete input;
delete ctxt;
}
catch (const xml::EIPRTFailure &err) { sThat->m->trappedErr.reset (stdx::new_exception_trap (err)); }
catch (...) { sThat->m->trappedErr.reset (stdx::new_exception_trap (xml::LogicError (RT_SRC_POS))); }
return NULL;
}
} /* namespace settings */