Settings.cpp revision 16714829db60c445b8710891811980372bbbbce4
/** @file
* Settings File Manipulation API.
*/
/*
* Copyright (C) 2007 innotek GmbH
*
* 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 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.
*/
#include "VBox/settings.h"
#include "Logging.h"
#include <libxml/xmlschemas.h>
#include <string.h>
/**
* Global module initialization structure.
*
* The constructor and destructor of this structure are used to perform global
* module initiaizaton and cleanup. Thee must be only one global variable of
* this structure.
*/
static
class Global
{
public:
Global()
{
/* Check the parser version. The docs say it will kill the app if
* there is a serious version mismatch, but I couldn't find it in the
* let's leave it as is for informational purposes. */
/* Init libxml */
/* Save the default entity resolver before someone has replaced it */
}
~Global()
{
/* Shutdown libxml */
}
struct
{
}
xml;
}
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:
throw ENotImplemented (RT_SRC_POS);
}
if (aSigned)
{
if (RT_SUCCESS (vrc))
{
}
}
else
{
if (RT_SUCCESS (vrc))
{
return result;
}
}
}
{
throw ENoValue();
return true;
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:
throw ENotImplemented (RT_SRC_POS);
}
if (RT_SUCCESS (vrc))
return result;
throw EIPRTFailure (vrc);
}
unsigned int aExtra /* = 0 */)
{
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;
}
//////////////////////////////////////////////////////////////////////////////
// File Class
//////////////////////////////////////////////////////////////////////////////
{
Data()
char *fileName;
bool opened : 1;
};
: m (new Data())
{
throw ENoMemory();
unsigned flags = 0;
switch (aMode)
{
case Read:
break;
case Write:
break;
case ReadWrite:
}
if (RT_FAILURE (vrc))
throw EIPRTFailure (vrc);
m->opened = true;
}
: m (new Data())
{
if (aHandle == NIL_RTFILE)
throw EInvalidArg (RT_SRC_POS);
if (aFileName)
{
throw ENoMemory();
}
setPos (0);
}
{
if (m->opened)
RTFileClose (m->handle);
}
{
return m->fileName;
}
{
uint64_t p = 0;
if (RT_SUCCESS (vrc))
return p;
throw EIPRTFailure (vrc);
}
{
uint64_t p = 0;
unsigned method = RTFILE_SEEK_BEGIN;
int vrc = VINF_SUCCESS;
/* check if we overflow int64_t and move to INT64_MAX first */
{
}
/* seek the rest */
if (RT_SUCCESS (vrc))
if (RT_SUCCESS (vrc))
return;
throw EIPRTFailure (vrc);
}
{
if (RT_SUCCESS (vrc))
return len;
throw EIPRTFailure (vrc);
}
{
if (RT_SUCCESS (vrc))
return len;
throw EIPRTFailure (vrc);
return -1 /* failure */;
}
{
if (RT_SUCCESS (vrc))
return;
throw EIPRTFailure (vrc);
}
//////////////////////////////////////////////////////////////////////////////
// MemoryBuf Class
//////////////////////////////////////////////////////////////////////////////
{
Data()
const char *buf;
char *uri;
};
: m (new Data())
{
throw EInvalidArg (RT_SRC_POS);
}
{
}
{
return m->uri;
}
{
return m->pos;
}
{
throw EInvalidArg();
throw EInvalidArg();
}
{
return 0 /* nothing to read */;
return len;
}
//////////////////////////////////////////////////////////////////////////////
// XmlKeyBackend Class
//////////////////////////////////////////////////////////////////////////////
{
public:
~XmlKeyBackend();
const char *name() const;
void zap();
private:
friend class XmlTreeBackend;
};
{
}
{
}
const char *XmlKeyBackend::name() const
{
}
{
throw ENotImplemented (RT_SRC_POS);
}
{
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;
{
{
throw ENoMemory();
}
/* outdate the node text holder */
{
}
return;
}
{
/* remove the attribute if it exists */
{
if (rc != 0)
throw EInvalidArg (RT_SRC_POS);
}
return;
}
throw ENoMemory();
}
{
if (!mNode)
return list;
{
{
}
}
return list;
}
{
if (!mNode)
return key;
{
{
{
break;
}
}
}
return key;
}
{
if (!mNode)
return Key();
throw ENoMemory();
}
void XmlKeyBackend::zap()
{
if (!mNode)
return;
xmlFreeNode (mNode);
}
//////////////////////////////////////////////////////////////////////////////
// XmlTreeBackend Class
//////////////////////////////////////////////////////////////////////////////
{
public:
{
if (!aErr)
throw EInvalidArg (RT_SRC_POS);
}
/**
* 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) {}
/**
* 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();
throw ENoMemory();
}
{
reset();
xmlFreeParserCtxt (m->ctxt);
}
{
m->inputResolver = &aResolver;
}
void XmlTreeBackend::resetInputResolver()
{
m->inputResolver = NULL;
}
int aFlags /* = 0 */)
{
/* 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()). */
/* Set up the external entity resolver. Note that we do it in a
* thread-unsafe fashion because this stuff is not thread-safe in libxml2.
* Making it thread-safe would require a) guarding this method with a
* mutex and b) requiring our API caller not to use libxml2 on some other
* thread (which is not practically possible). So, our API is not
* thread-safe for now. */
sThat = this;
/* 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 */
{
/* restore the previous entity resolver */
/* look if there was a forwared exception from the lower level */
m->trappedErr->rethrow();
}
{
/* validate the document */
try
{
bool valid = false;
if (schemaCtxt == NULL)
throw LogicError (RT_SRC_POS);
/* set our error handlers */
&errorStr);
/* load schema */
{
throw LogicError (RT_SRC_POS);
/* 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();
throw LogicError (RT_SRC_POS);
/* errorStr is freed in catch(...) below */
}
}
catch (...)
{
/* restore the previous entity resolver */
if (validCtxt)
if (schema)
if (schemaCtxt)
throw;
}
}
/* restore the previous entity resolver */
/* reset the previous tree on success */
reset();
/* assign the root key */
}
{
/* 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;
throw LogicError (RT_SRC_POS);
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. */
throw LogicError (RT_SRC_POS);
}
}
void XmlTreeBackend::reset()
{
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;
{
}
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;
throw ENoMemory();
}
return NULL;
}
} /* namespace settings */