Index: ext/libicpf/libicpf.vc71.vcproj =================================================================== diff -u -ree851c181bb121b5e7062107c51016ed97e75611 -r3e7ccb6efc5089399a7801082d923aeb41f53d2e --- ext/libicpf/libicpf.vc71.vcproj (.../libicpf.vc71.vcproj) (revision ee851c181bb121b5e7062107c51016ed97e75611) +++ ext/libicpf/libicpf.vc71.vcproj (.../libicpf.vc71.vcproj) (revision 3e7ccb6efc5089399a7801082d923aeb41f53d2e) @@ -122,6 +122,9 @@ RelativePath=".\src\cfg.cpp"> + + + + + + +#include "exception.h" +#include +#include + +BEGIN_ICPF_NAMESPACE + +/// Buffer size for reading xml data from a file +#define XML_BUFFER 65536 + +/// Definition of a standard string depending on the unicode support +#ifdef _UNICODE + #define tstring std::wstring +#else + #define tstring std::string +#endif + +// forward declaration +class xml_node; + +/// Xml node storage +typedef std::map xml_storage; +/// String storage (key(s)=>value(s)) +typedef std::multimap attr_storage; + +/** Class manages a single xml node. + */ +class xml_node +{ +public: +/** \name Construction/destruction */ +/**@{*/ + /// Standard constructor + xml_node() : m_mNodes(), m_mAttr(), m_pParentNode(NULL) { }; + /// Constructor defining the parent node + xml_node(xml_node* pParentNode) : m_mNodes(), m_mAttr(), m_pParentNode(pParentNode) { }; +/**@}*/ + +public: + xml_storage m_mNodes; ///< Additional nodes inside of this one + attr_storage m_mAttr; ///< String pairs belonging to this node + xml_node* m_pParentNode; ///< Parent node +}; + +/** State structure - used by expat notifications. + */ +struct XMLSTATE +{ + xml_cfg* pCfg; + xml_node* pNode; +}; + +/** Xml find handle structure - used for searching. + */ +struct XMLFINDHANDLE +{ + attr_storage::iterator it; ///< Iterator of currently retrieved string + attr_storage::iterator itEnd; ///< Iterator of a last string matching the criteria +}; + +/// Macro for faster access to the xml storage +#define m_pStorage ((xml_storage*)m_hStorage) + +/** Constructs the xml_cfg object. + */ +xml_cfg::xml_cfg() : + m_hStorage((ptr_t)new xml_storage) +{ + +} + +/** Destructs the xml config object. + */ +xml_cfg::~xml_cfg() +{ + delete m_pStorage; +} + +/** Expat start element handler. + * + * \param[in] userData - pointer to user defined parameters + * \param[in] name - name of the tag being processed + * \param[in] attrs - array of pointers to strings with attributes and their values + */ +void XMLCALL element_start(void *userData, const XML_Char *name, const XML_Char **attrs) +{ + XMLSTATE* pState=(XMLSTATE*)userData; + + bool bContainer=true; + + for (size_t t=0;attrs[t] != NULL;t+=2) + { + if (_tcscmp(attrs[t], _t("value")) == 0) + { + // this is the value type tag + pState->pNode->m_mAttr.insert(attr_storage::value_type(tstring(name), tstring(attrs[t+1]))); + bContainer=false; + } + } + + if (bContainer) + { + std::pair pr=pState->pNode->m_mNodes.insert(xml_storage::value_type(tstring(name), xml_node(pState->pNode))); + pState->pNode=&((*pr.first).second); + } +} + +/** Expat handler for closing tag. + * + * \param[in] userData - user defined parameter + * \param[in] name - name of the tag being closed + */ +void XMLCALL element_end(void *userData, const XML_Char* /*name*/) +{ + XMLSTATE* pState=(XMLSTATE*)userData; + + // go up one level + if (pState->pNode) + pState->pNode=pState->pNode->m_pParentNode; + else + THROW(_t("Trying to close non-existent tag."), 0, 0, 0); +} + +/*void XMLCALL element_content(void *userData, const XML_Char *s, int len) +{ + XMLSTATE* pState=(XMLSTATE*)userData; + +}*/ + +/** Function reads the contents of the xml file, parses it using expat parser + * and then creates xml nodes in memory that could be read using find functions. + * + * \param[in] pszPath - path to the file to be read + */ +void xml_cfg::read(const tchar_t* pszPath) +{ + // read the data from file in 64kB portions and feed it to the expat xml parser + FILE* pFile=_tfopen(pszPath, _t("r")); + if (pFile == NULL) + THROW(icpf::exception::format(_t("Cannot open the file ") STRFMT _t("."), pszPath), 0, errno, 0); + + // create the parser + XML_Parser parser=XML_ParserCreate(NULL); + XML_SetElementHandler(parser, element_start, element_end); +// XML_SetCharacterDataHandler(parser, element_content); + + XMLSTATE xs = { this }; + XML_SetUserData(parser, &xs); + + for (;;) + { + bool bLast=false; + + // get xml buffer + void* pBuffer=XML_GetBuffer(parser, XML_BUFFER); + + // read some data to it + size_t tSize=fread(pBuffer, 1, XML_BUFFER, pFile); + if (tSize < XML_BUFFER) + { + // check for errors + int iErr=0; + if ( (iErr=ferror(pFile)) != 0) + THROW(icpf::exception::format(_t("Error reading from the file ") STRFMT _t("."), pszPath), 0, iErr, 0); + else + bLast=true; + } + + // parse + if (!XML_ParseBuffer(parser, (int)tSize, bLast)) + { + // parser error + THROW(icpf::exception::format(_t("Error encountered while parsing the xml file ") STRFMT _t(" - ") STRFMT _t("."), pszPath, XML_ErrorString(XML_GetErrorCode(parser))), 0, 0, 0); + } + + // end of processing ? + if (bLast) + break; + } + + // free parser + XML_ParserFree(parser); + + // close the file + fclose(pFile); +} + +/** Saves the internal xml nodes to the specified xml file. + * + * \param[in] pszPath - path to the file the data should be written to + * + * \note Function overwrites the contents of a file + */ +void xml_cfg::save(const tchar_t* /*pszPath*/) +{ + +} + +/** Function starts a search operation. Given the name of the property + * to be searched for (ie. "ch/program/startup"), funtion searches for + * it and returns a handle that can be used by subsequent calls to the + * find_next(). Free the handle using find_close() after finish. + * + * \param[in] pszName - name of the property to search for (in the form of + * "ch/program/startup" for xml such as this: + * + * + * + * + * + * + * \return Handle to the search (NULL if not found). + */ +ptr_t xml_cfg::find(const tchar_t* pszName) +{ + return find(m_pStorage, pszName); +} + +/** A find() helper function - recursively searches a specific node + * for a given name. + * + * \param[in] pNodePtr - pointer to a node to search in + * \param[in] pszName - name of the property to search for + * \return Handle to the node or NULL if none. + */ +ptr_t xml_cfg::find(ptr_t pNodePtr, const tchar_t* pszName) +{ + xml_node* pNode=(xml_node*)pNodePtr; + + // parse the name + tchar_t* pSign=_tcschr(pszName, _t('/')); + if (pSign) + { + // locate the xml_node associated with the name + xml_storage::iterator it=pNode->m_mNodes.find(tstring(pszName, pSign-pszName)); + if (it != pNode->m_mNodes.end()) + return find(&(*it).second, pSign+1); + else + return NULL; + } + else + { + XMLFINDHANDLE* pfh=new XMLFINDHANDLE; + std::pair pr=pNode->m_mAttr.equal_range(pszName); + pfh->it=pr.first; + pfh->itEnd=pr.second; + + return pfh; + } +} + +/** Finds the next string that belong to a specific key (as defined in + * a call to find() function. + * + * \param[in] pFindHandle - handle to the search (as returned from find()) + * \return Pointer to a next string found, NULL if none. + */ +const tchar_t* xml_cfg::find_next(ptr_t pFindHandle) +{ + XMLFINDHANDLE* pfh=(XMLFINDHANDLE*)pFindHandle; + if (pfh->it != pfh->itEnd) + return (*pfh->it++).second.c_str(); + else + return NULL; +} + +/** Closes the find handle. + * + * \param[in] pFindHandle - handle to the search (as returned from find()) + */ +void xml_cfg::find_close(ptr_t pFindHandle) +{ + delete ((XMLFINDHANDLE*)pFindHandle); +} + +/** Sets the specified value in the given key name. Value can be either added to + * the current ones (multi-string support) or replace them completely. + * + * \param[in] pszName - key name for which the string should be set at + * \param[in] pszValue - value to set + * \param[in] bAdd - true if the string should be added to the previous, false if replaced. + */ +void xml_cfg::set_value(const tchar_t* pszName, const tchar_t* pszValue, bool bAdd) +{ + // traverse the current tag tree + set_value(m_pStorage, pszName, pszValue, bAdd); +} + +/** Sets the specified value in the given key name - recursive helper function. + * + * \param[in] pNodePtr - pointer to the xml node to process + * \param[in] pszName - key name for which the string should be set at + * \param[in] pszValue - value to set + * \param[in] bAdd - true if the string should be added to the previous, false if replaced. + */ +void xml_cfg::set_value(ptr_t pNodePtr, const tchar_t* pszName, const tchar_t* pszValue, bool bAdd) +{ + xml_node* pNode=(xml_node*)pNodePtr; + + tchar_t* pszSign=_tcschr(pszName, _t('/')); + if (pszSign != NULL) + { + xml_storage::iterator it=pNode->m_mNodes.find(tstring(pszName, pszSign-pszName)); + if (it != pNode->m_mNodes.end()) + set_value(&(*it).second, pszSign+1, pszValue, bAdd); + else + { + std::pair pr=pNode->m_mNodes.insert(xml_storage::value_type(tstring(pszName, pszSign-pszName), xml_node(pNode))); + set_value(&(*pr.first).second, pszSign+1, pszValue, bAdd); + } + } + else + { + // now it's the key going + if (pszValue == NULL) + { + if (bAdd) + pNode->m_mAttr.erase(tstring(pszName)); // remove the key from map + else + pNode->m_mAttr.clear(); // clear the entire map + } + else + { + // clear if we're replacing + if (!bAdd) + pNode->m_mAttr.clear(); + + // and add + pNode->m_mAttr.insert(attr_storage::value_type(tstring(pszName), tstring(pszValue))); + } + } +} + +END_ICPF_NAMESPACE Index: ext/libicpf/src/cfg_xml.h =================================================================== diff -u --- ext/libicpf/src/cfg_xml.h (revision 0) +++ ext/libicpf/src/cfg_xml.h (revision 3e7ccb6efc5089399a7801082d923aeb41f53d2e) @@ -0,0 +1,57 @@ +#ifndef __CFGXML_H__ +#define __CFGXML_H__ + +#include "gen_types.h" +#include "libicpf.h" +#include "config_base.h" + +BEGIN_ICPF_NAMESPACE + +/** Class provides the necessary base handlers for config class. + * It handles the xml data streams contained in the files, providing + * a way to set and retrieve data contained in the xml document. + */ +class xml_cfg : public config_base +{ +public: +/** \name Construction/destruction/operators */ +/**@{*/ + xml_cfg(); ///< Standard constructor + xml_cfg(const xml_cfg& rSrc); ///< Copy construtor + virtual ~xml_cfg(); ///< Standard destructor +/**@}*/ + +/** \name File operations */ +/**@{*/ + /// Reads the xml document from the specified file + virtual void read(const tchar_t* pszPath); + /// Saves the internal data to a specified file as the xml document + virtual void save(const tchar_t* pszPath); +/**@}*/ + +/** \name Key and value handling */ +/**@{*/ + /// Searches for a specified key (given all the path to a specific string) + virtual ptr_t find(const tchar_t* pszName); + /// Searches for the next string + virtual const tchar_t* find_next(ptr_t pFindHandle); + /// Closes the search operation + virtual void find_close(ptr_t pFindHandle); + + /// Sets a value for a given key (either adds to or replaces the previous value) + virtual void set_value(const tchar_t* pszName, const tchar_t* pszValue, bool bAdd); +/**@}*/ + +private: + /// Find helper - recursively searches for a specific key node + ptr_t find(ptr_t pNodePtr, const tchar_t* pszName); + /// Set value helper - searches for a specific node and sets the value + void set_value(ptr_t pNodePtr, const tchar_t* pszName, const tchar_t* pszValue, bool bAdd); + +protected: + ptr_t m_hStorage; ///< Handle to the internal xml storage +}; + +END_ICPF_NAMESPACE + +#endif Index: ext/libicpf/src/config_base.h =================================================================== diff -u --- ext/libicpf/src/config_base.h (revision 0) +++ ext/libicpf/src/config_base.h (revision 3e7ccb6efc5089399a7801082d923aeb41f53d2e) @@ -0,0 +1,36 @@ +#ifndef __CONFIG_BASE_H__ +#define __CONFIG_BASE_H__ + +#include "gen_types.h" +#include "libicpf.h" + +BEGIN_ICPF_NAMESPACE + +class config_base +{ +public: +/** \name File operations */ +/**@{*/ + /// Reads the xml document from the specified file + virtual void read(const tchar_t* pszPath) = 0; + /// Saves the internal data to a specified file as the xml document + virtual void save(const tchar_t* pszPath) = 0; +/**@}*/ + +/** \name Key and value handling */ +/**@{*/ + /// Searches for a specified key (given all the path to a specific string) + virtual ptr_t find(const tchar_t* pszName) = 0; + /// Searches for the next string + virtual const tchar_t* find_next(ptr_t pFindHandle) = 0; + /// Closes the search operation + virtual void find_close(ptr_t pFindHandle) = 0; + + /// Sets a value for a given key (either adds to or replaces the previous value) + virtual void set_value(const tchar_t* pszName, const tchar_t* pszValue, bool bAdd) = 0; +/**@}*/ +}; + +END_ICPF_NAMESPACE + +#endif