diff options
author | Brodie Thiesfield <brofield@gmail.com> | 2009-06-30 12:53:17 +0400 |
---|---|---|
committer | Brodie Thiesfield <brofield@gmail.com> | 2009-06-30 12:53:17 +0400 |
commit | 6cfac4124bdd0fd2a4bef9741cd76747b76a6893 (patch) | |
tree | d7951d7559f47efec8eb5aba7adb46fe01f0acd0 | |
parent | a93d4542d810c73d0450b1bd9a88dde3655d336e (diff) | |
parent | ed8fa1a6a7db8d0be9b9d39e812f34c1f0f0da10 (diff) |
include latest update
-rw-r--r-- | SimpleIni.h | 6462 |
1 files changed, 3231 insertions, 3231 deletions
diff --git a/SimpleIni.h b/SimpleIni.h index a2c9fc4..ae42d63 100644 --- a/SimpleIni.h +++ b/SimpleIni.h @@ -1,3232 +1,3232 @@ -/** @mainpage - - <table> - <tr><th>Library <td>SimpleIni - <tr><th>File <td>SimpleIni.h - <tr><th>Author <td>Brodie Thiesfield [code at jellycan dot com] - <tr><th>Source <td>http://code.jellycan.com/simpleini/ - <tr><th>Version <td>4.11 - </table> - - Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation. - - @section intro INTRODUCTION - - This component allows an INI-style configuration file to be used on both - Windows and Linux/Unix. It is fast, simple and source code using this - component will compile unchanged on either OS. - - - @section features FEATURES - - - MIT Licence allows free use in all software (including GPL and commercial) - - multi-platform (Windows 95/98/ME/NT/2K/XP/2003, Windows CE, Linux, Unix) - - loading and saving of INI-style configuration files - - configuration files can have any newline format on all platforms - - liberal acceptance of file format - - key/values with no section - - removal of whitespace around sections, keys and values - - support for multi-line values (values with embedded newline characters) - - optional support for multiple keys with the same name - - optional case-insensitive sections and keys (for ASCII characters only) - - saves files with sections and keys in the same order as they were loaded - - preserves comments on the file, section and keys where possible. - - supports both char or wchar_t programming interfaces - - supports both MBCS (system locale) and UTF-8 file encodings - - system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file - - support for non-ASCII characters in section, keys, values and comments - - support for non-standard character types or file encodings - via user-written converter classes - - support for adding/modifying values programmatically - - compiles cleanly in the following compilers: - - Windows/VC6 (warning level 3) - - Windows/VC.NET 2003 (warning level 4) - - Windows/VC 2005 (warning level 4) - - Linux/gcc (-Wall) - - - @section usage USAGE SUMMARY - - -# Define the appropriate symbol for the converter you wish to use and - include the SimpleIni.h header file. If no specific converter is defined - then the default converter is used. The default conversion mode uses - SI_CONVERT_WIN32 on Windows and SI_CONVERT_GENERIC on all other - platforms. If you are using ICU then SI_CONVERT_ICU is supported on all - platforms. - -# Declare an instance the appropriate class. Note that the following - definitions are just shortcuts for commonly used types. Other types - (PRUnichar, unsigned short, unsigned char) are also possible. - <table> - <tr><th>Interface <th>Case-sensitive <th>Load UTF-8 <th>Load MBCS <th>Typedef - <tr><th>SI_CONVERT_GENERIC - <tr><td>char <td>No <td>Yes <td>Yes #1 <td>CSimpleIniA - <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA - <tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW - <tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW - <tr><th>SI_CONVERT_WIN32 - <tr><td>char <td>No <td>No #2 <td>Yes <td>CSimpleIniA - <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA - <tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW - <tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW - <tr><th>SI_CONVERT_ICU - <tr><td>char <td>No <td>Yes <td>Yes <td>CSimpleIniA - <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA - <tr><td>UChar <td>No <td>Yes <td>Yes <td>CSimpleIniW - <tr><td>UChar <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW - </table> - #1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.<br> - #2 Only affects Windows. On Windows this uses MBCS functions and - so may fold case incorrectly leading to uncertain results. - -# Call Load() or LoadFile() to load and parse the INI configuration file - -# Access and modify the data of the file using the following functions - <table> - <tr><td>GetAllSections <td>Return all section names - <tr><td>GetAllKeys <td>Return all key names within a section - <tr><td>GetAllValues <td>Return all values within a section & key - <tr><td>GetSection <td>Return all key names and values in a section - <tr><td>GetSectionSize <td>Return the number of keys in a section - <tr><td>GetValue <td>Return a value for a section & key - <tr><td>SetValue <td>Add or update a value for a section & key - <tr><td>Delete <td>Remove a section, or a key from a section - </table> - -# Call Save() or SaveFile() to save the INI configuration data - - @section iostreams IO STREAMS - - SimpleIni supports reading from and writing to STL IO streams. Enable this - by defining SI_SUPPORT_IOSTREAMS before including the SimpleIni.h header - file. Ensure that if the streams are backed by a file (e.g. ifstream or - ofstream) then the flag ios_base::binary has been used when the file was - opened. - - @section multiline MULTI-LINE VALUES - - Values that span multiple lines are created using the following format. - - <pre> - key = <<<ENDTAG - .... multiline value .... - ENDTAG - </pre> - - Note the following: - - The text used for ENDTAG can be anything and is used to find - where the multi-line text ends. - - The newline after ENDTAG in the start tag, and the newline - before ENDTAG in the end tag is not included in the data value. - - The ending tag must be on it's own line with no whitespace before - or after it. - - The multi-line value is modified at load so that each line in the value - is delimited by a single '\\n' character on all platforms. At save time - it will be converted into the newline format used by the current - platform. - - @section comments COMMENTS - - Comments are preserved in the file within the following restrictions: - - Every file may have a single "file comment". It must start with the - first character in the file, and will end with the first non-comment - line in the file. - - Every section may have a single "section comment". It will start - with the first comment line following the file comment, or the last - data entry. It ends at the beginning of the section. - - Every key may have a single "key comment". This comment will start - with the first comment line following the section start, or the file - comment if there is no section name. - - Comments are set at the time that the file, section or key is first - created. The only way to modify a comment on a section or a key is to - delete that entry and recreate it with the new comment. There is no - way to change the file comment. - - @section save SAVE ORDER - - The sections and keys are written out in the same order as they were - read in from the file. Sections and keys added to the data after the - file has been loaded will be added to the end of the file when it is - written. There is no way to specify the location of a section or key - other than in first-created, first-saved order. - - @section notes NOTES - - - To load UTF-8 data on Windows 95, you need to use Microsoft Layer for - Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU. - - When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked. - - When using SI_CONVERT_ICU, ICU header files must be on the include - path and icuuc.lib must be linked in. - - To load a UTF-8 file on Windows AND expose it with SI_CHAR == char, - you should use SI_CONVERT_GENERIC. - - The collation (sorting) order used for sections and keys returned from - iterators is NOT DEFINED. If collation order of the text is important - then it should be done yourself by either supplying a replacement - SI_STRLESS class, or by sorting the strings external to this library. - - Usage of the <mbstring.h> header on Windows can be disabled by defining - SI_NO_MBCS. This is defined automatically on Windows CE platforms. - - - @section licence MIT LICENCE - - The licence text below is the boilerplate "MIT Licence" used from: - http://www.opensource.org/licenses/mit-license.php - - Copyright (c) 2006-2008, Brodie Thiesfield - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is furnished - to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef INCLUDED_SimpleIni_h -#define INCLUDED_SimpleIni_h - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -// Disable these warnings in MSVC: -// 4127 "conditional expression is constant" as the conversion classes trigger -// it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will -// be optimized away in a release build. -// 4503 'insert' : decorated name length exceeded, name was truncated -// 4702 "unreachable code" as the MS STL header causes it in release mode. -// Again, the code causing the warning will be cleaned up by the compiler. -// 4786 "identifier truncated to 256 characters" as this is thrown hundreds -// of times VC6 as soon as STL is used. -#ifdef _MSC_VER -# pragma warning (push) -# pragma warning (disable: 4127 4503 4702 4786) -#endif - +/** @mainpage
+
+ <table>
+ <tr><th>Library <td>SimpleIni
+ <tr><th>File <td>SimpleIni.h
+ <tr><th>Author <td>Brodie Thiesfield [code at jellycan dot com]
+ <tr><th>Source <td>http://code.jellycan.com/simpleini/
+ <tr><th>Version <td>4.11
+ </table>
+
+ Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation.
+
+ @section intro INTRODUCTION
+
+ This component allows an INI-style configuration file to be used on both
+ Windows and Linux/Unix. It is fast, simple and source code using this
+ component will compile unchanged on either OS.
+
+
+ @section features FEATURES
+
+ - MIT Licence allows free use in all software (including GPL and commercial)
+ - multi-platform (Windows 95/98/ME/NT/2K/XP/2003, Windows CE, Linux, Unix)
+ - loading and saving of INI-style configuration files
+ - configuration files can have any newline format on all platforms
+ - liberal acceptance of file format
+ - key/values with no section
+ - removal of whitespace around sections, keys and values
+ - support for multi-line values (values with embedded newline characters)
+ - optional support for multiple keys with the same name
+ - optional case-insensitive sections and keys (for ASCII characters only)
+ - saves files with sections and keys in the same order as they were loaded
+ - preserves comments on the file, section and keys where possible.
+ - supports both char or wchar_t programming interfaces
+ - supports both MBCS (system locale) and UTF-8 file encodings
+ - system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file
+ - support for non-ASCII characters in section, keys, values and comments
+ - support for non-standard character types or file encodings
+ via user-written converter classes
+ - support for adding/modifying values programmatically
+ - compiles cleanly in the following compilers:
+ - Windows/VC6 (warning level 3)
+ - Windows/VC.NET 2003 (warning level 4)
+ - Windows/VC 2005 (warning level 4)
+ - Linux/gcc (-Wall)
+
+
+ @section usage USAGE SUMMARY
+
+ -# Define the appropriate symbol for the converter you wish to use and
+ include the SimpleIni.h header file. If no specific converter is defined
+ then the default converter is used. The default conversion mode uses
+ SI_CONVERT_WIN32 on Windows and SI_CONVERT_GENERIC on all other
+ platforms. If you are using ICU then SI_CONVERT_ICU is supported on all
+ platforms.
+ -# Declare an instance the appropriate class. Note that the following
+ definitions are just shortcuts for commonly used types. Other types
+ (PRUnichar, unsigned short, unsigned char) are also possible.
+ <table>
+ <tr><th>Interface <th>Case-sensitive <th>Load UTF-8 <th>Load MBCS <th>Typedef
+ <tr><th>SI_CONVERT_GENERIC
+ <tr><td>char <td>No <td>Yes <td>Yes #1 <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
+ <tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW
+ <tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
+ <tr><th>SI_CONVERT_WIN32
+ <tr><td>char <td>No <td>No #2 <td>Yes <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
+ <tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW
+ <tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
+ <tr><th>SI_CONVERT_ICU
+ <tr><td>char <td>No <td>Yes <td>Yes <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
+ <tr><td>UChar <td>No <td>Yes <td>Yes <td>CSimpleIniW
+ <tr><td>UChar <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
+ </table>
+ #1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.<br>
+ #2 Only affects Windows. On Windows this uses MBCS functions and
+ so may fold case incorrectly leading to uncertain results.
+ -# Call Load() or LoadFile() to load and parse the INI configuration file
+ -# Access and modify the data of the file using the following functions
+ <table>
+ <tr><td>GetAllSections <td>Return all section names
+ <tr><td>GetAllKeys <td>Return all key names within a section
+ <tr><td>GetAllValues <td>Return all values within a section & key
+ <tr><td>GetSection <td>Return all key names and values in a section
+ <tr><td>GetSectionSize <td>Return the number of keys in a section
+ <tr><td>GetValue <td>Return a value for a section & key
+ <tr><td>SetValue <td>Add or update a value for a section & key
+ <tr><td>Delete <td>Remove a section, or a key from a section
+ </table>
+ -# Call Save() or SaveFile() to save the INI configuration data
+
+ @section iostreams IO STREAMS
+
+ SimpleIni supports reading from and writing to STL IO streams. Enable this
+ by defining SI_SUPPORT_IOSTREAMS before including the SimpleIni.h header
+ file. Ensure that if the streams are backed by a file (e.g. ifstream or
+ ofstream) then the flag ios_base::binary has been used when the file was
+ opened.
+
+ @section multiline MULTI-LINE VALUES
+
+ Values that span multiple lines are created using the following format.
+
+ <pre>
+ key = <<<ENDTAG
+ .... multiline value ....
+ ENDTAG
+ </pre>
+
+ Note the following:
+ - The text used for ENDTAG can be anything and is used to find
+ where the multi-line text ends.
+ - The newline after ENDTAG in the start tag, and the newline
+ before ENDTAG in the end tag is not included in the data value.
+ - The ending tag must be on it's own line with no whitespace before
+ or after it.
+ - The multi-line value is modified at load so that each line in the value
+ is delimited by a single '\\n' character on all platforms. At save time
+ it will be converted into the newline format used by the current
+ platform.
+
+ @section comments COMMENTS
+
+ Comments are preserved in the file within the following restrictions:
+ - Every file may have a single "file comment". It must start with the
+ first character in the file, and will end with the first non-comment
+ line in the file.
+ - Every section may have a single "section comment". It will start
+ with the first comment line following the file comment, or the last
+ data entry. It ends at the beginning of the section.
+ - Every key may have a single "key comment". This comment will start
+ with the first comment line following the section start, or the file
+ comment if there is no section name.
+ - Comments are set at the time that the file, section or key is first
+ created. The only way to modify a comment on a section or a key is to
+ delete that entry and recreate it with the new comment. There is no
+ way to change the file comment.
+
+ @section save SAVE ORDER
+
+ The sections and keys are written out in the same order as they were
+ read in from the file. Sections and keys added to the data after the
+ file has been loaded will be added to the end of the file when it is
+ written. There is no way to specify the location of a section or key
+ other than in first-created, first-saved order.
+
+ @section notes NOTES
+
+ - To load UTF-8 data on Windows 95, you need to use Microsoft Layer for
+ Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU.
+ - When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked.
+ - When using SI_CONVERT_ICU, ICU header files must be on the include
+ path and icuuc.lib must be linked in.
+ - To load a UTF-8 file on Windows AND expose it with SI_CHAR == char,
+ you should use SI_CONVERT_GENERIC.
+ - The collation (sorting) order used for sections and keys returned from
+ iterators is NOT DEFINED. If collation order of the text is important
+ then it should be done yourself by either supplying a replacement
+ SI_STRLESS class, or by sorting the strings external to this library.
+ - Usage of the <mbstring.h> header on Windows can be disabled by defining
+ SI_NO_MBCS. This is defined automatically on Windows CE platforms.
+
+
+ @section licence MIT LICENCE
+
+ The licence text below is the boilerplate "MIT Licence" used from:
+ http://www.opensource.org/licenses/mit-license.php
+
+ Copyright (c) 2006-2008, Brodie Thiesfield
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is furnished
+ to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef INCLUDED_SimpleIni_h
+#define INCLUDED_SimpleIni_h
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+// Disable these warnings in MSVC:
+// 4127 "conditional expression is constant" as the conversion classes trigger
+// it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
+// be optimized away in a release build.
+// 4503 'insert' : decorated name length exceeded, name was truncated
+// 4702 "unreachable code" as the MS STL header causes it in release mode.
+// Again, the code causing the warning will be cleaned up by the compiler.
+// 4786 "identifier truncated to 256 characters" as this is thrown hundreds
+// of times VC6 as soon as STL is used.
+#ifdef _MSC_VER
+# pragma warning (push)
+# pragma warning (disable: 4127 4503 4702 4786)
+#endif
+
#include <cstring>
-#include <string> -#include <map> -#include <list> -#include <algorithm> -#include <stdio.h> - -#ifdef SI_SUPPORT_IOSTREAMS -# include <iostream> -#endif // SI_SUPPORT_IOSTREAMS - -#ifdef _DEBUG -# ifndef assert -# include <cassert> -# endif -# define SI_ASSERT(x) assert(x) -#else -# define SI_ASSERT(x) -#endif - -enum SI_Error { - SI_OK = 0, //!< No error - SI_UPDATED = 1, //!< An existing value was updated - SI_INSERTED = 2, //!< A new value was inserted - - // note: test for any error with (retval < 0) - SI_FAIL = -1, //!< Generic failure - SI_NOMEM = -2, //!< Out of memory error - SI_FILE = -3 //!< File error (see errno for detail error) -}; - -#define SI_UTF8_SIGNATURE "\xEF\xBB\xBF" - -#ifdef _WIN32 -# define SI_NEWLINE_A "\r\n" -# define SI_NEWLINE_W L"\r\n" -#else // !_WIN32 -# define SI_NEWLINE_A "\n" -# define SI_NEWLINE_W L"\n" -#endif // _WIN32 - -#if defined(SI_CONVERT_ICU) -# include <unicode/ustring.h> -#endif - -#if defined(_WIN32) -# define SI_HAS_WIDE_FILE -# define SI_WCHAR_T wchar_t -#elif defined(SI_CONVERT_ICU) -# define SI_HAS_WIDE_FILE -# define SI_WCHAR_T UChar -#endif - - -// --------------------------------------------------------------------------- -// MAIN TEMPLATE CLASS -// --------------------------------------------------------------------------- - -/** Simple INI file reader. - - This can be instantiated with the choice of unicode or native characterset, - and case sensitive or insensitive comparisons of section and key names. - The supported combinations are pre-defined with the following typedefs: - - <table> - <tr><th>Interface <th>Case-sensitive <th>Typedef - <tr><td>char <td>No <td>CSimpleIniA - <tr><td>char <td>Yes <td>CSimpleIniCaseA - <tr><td>wchar_t <td>No <td>CSimpleIniW - <tr><td>wchar_t <td>Yes <td>CSimpleIniCaseW - </table> - - Note that using other types for the SI_CHAR is supported. For instance, - unsigned char, unsigned short, etc. Note that where the alternative type - is a different size to char/wchar_t you may need to supply new helper - classes for SI_STRLESS and SI_CONVERTER. - */ -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -class CSimpleIniTempl -{ -public: - /** key entry */ - struct Entry { - const SI_CHAR * pItem; - const SI_CHAR * pComment; - int nOrder; - - Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0) - : pItem(a_pszItem) - , pComment(NULL) - , nOrder(a_nOrder) - { } - Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder) - : pItem(a_pszItem) - , pComment(a_pszComment) - , nOrder(a_nOrder) - { } - Entry(const Entry & rhs) { operator=(rhs); } - Entry & operator=(const Entry & rhs) { - pItem = rhs.pItem; - pComment = rhs.pComment; - nOrder = rhs.nOrder; - return *this; - } - -#if defined(_MSC_VER) && _MSC_VER <= 1200 - /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */ - bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); } - bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); } -#endif - - /** Strict less ordering by name of key only */ - struct KeyOrder : std::binary_function<Entry, Entry, bool> { - bool operator()(const Entry & lhs, const Entry & rhs) const { - const static SI_STRLESS isLess = SI_STRLESS(); - return isLess(lhs.pItem, rhs.pItem); - } - }; - - /** Strict less ordering by order, and then name of key */ - struct LoadOrder : std::binary_function<Entry, Entry, bool> { - bool operator()(const Entry & lhs, const Entry & rhs) const { - if (lhs.nOrder != rhs.nOrder) { - return lhs.nOrder < rhs.nOrder; - } - return KeyOrder()(lhs.pItem, rhs.pItem); - } - }; - }; - - /** map keys to values */ - typedef std::multimap<Entry,const SI_CHAR *,typename Entry::KeyOrder> TKeyVal; - - /** map sections to key/value map */ - typedef std::map<Entry,TKeyVal,typename Entry::KeyOrder> TSection; - - /** set of dependent string pointers. Note that these pointers are - dependent on memory owned by CSimpleIni. - */ - typedef std::list<Entry> TNamesDepend; - - /** interface definition for the OutputWriter object to pass to Save() - in order to output the INI file data. - */ - class OutputWriter { - public: - OutputWriter() { } - virtual ~OutputWriter() { } - virtual void Write(const char * a_pBuf) = 0; - private: - OutputWriter(const OutputWriter &); // disable - OutputWriter & operator=(const OutputWriter &); // disable - }; - - /** OutputWriter class to write the INI data to a file */ - class FileWriter : public OutputWriter { - FILE * m_file; - public: - FileWriter(FILE * a_file) : m_file(a_file) { } - void Write(const char * a_pBuf) { - fputs(a_pBuf, m_file); - } - private: - FileWriter(const FileWriter &); // disable - FileWriter & operator=(const FileWriter &); // disable - }; - - /** OutputWriter class to write the INI data to a string */ - class StringWriter : public OutputWriter { - std::string & m_string; - public: - StringWriter(std::string & a_string) : m_string(a_string) { } - void Write(const char * a_pBuf) { - m_string.append(a_pBuf); - } - private: - StringWriter(const StringWriter &); // disable - StringWriter & operator=(const StringWriter &); // disable - }; - -#ifdef SI_SUPPORT_IOSTREAMS - /** OutputWriter class to write the INI data to an ostream */ - class StreamWriter : public OutputWriter { - std::ostream & m_ostream; - public: - StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { } - void Write(const char * a_pBuf) { - m_ostream << a_pBuf; - } - private: - StreamWriter(const StreamWriter &); // disable - StreamWriter & operator=(const StreamWriter &); // disable - }; -#endif // SI_SUPPORT_IOSTREAMS - - /** Characterset conversion utility class to convert strings to the - same format as is used for the storage. - */ - class Converter : private SI_CONVERTER { - public: - Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) { - m_scratch.resize(1024); - } - Converter(const Converter & rhs) { operator=(rhs); } - Converter & operator=(const Converter & rhs) { - m_scratch = rhs.m_scratch; - return *this; - } - bool ConvertToStore(const SI_CHAR * a_pszString) { - size_t uLen = SizeToStore(a_pszString); - if (uLen == (size_t)(-1)) { - return false; - } - while (uLen > m_scratch.size()) { - m_scratch.resize(m_scratch.size() * 2); - } - return SI_CONVERTER::ConvertToStore( - a_pszString, - const_cast<char*>(m_scratch.data()), - m_scratch.size()); - } - const char * Data() { return m_scratch.data(); } - private: - std::string m_scratch; - }; - -public: - /*-----------------------------------------------------------------------*/ - - /** Default constructor. - - @param a_bIsUtf8 See the method SetUnicode() for details. - @param a_bMultiKey See the method SetMultiKey() for details. - @param a_bMultiLine See the method SetMultiLine() for details. - */ - CSimpleIniTempl( - bool a_bIsUtf8 = false, - bool a_bMultiKey = false, - bool a_bMultiLine = false - ); - - /** Destructor */ - ~CSimpleIniTempl(); - - /** Deallocate all memory stored by this object */ - void Reset(); - - /*-----------------------------------------------------------------------*/ - /** @{ @name Settings */ - - /** Set the storage format of the INI data. This affects both the loading - and saving of the INI data using all of the Load/Save API functions. - This value cannot be changed after any INI data has been loaded. - - If the file is not set to Unicode (UTF-8), then the data encoding is - assumed to be the OS native encoding. This encoding is the system - locale on Linux/Unix and the legacy MBCS encoding on Windows NT/2K/XP. - If the storage format is set to Unicode then the file will be loaded - as UTF-8 encoded data regardless of the native file encoding. If - SI_CHAR == char then all of the char* parameters take and return UTF-8 - encoded data regardless of the system locale. - - \param a_bIsUtf8 Assume UTF-8 encoding for the source? - */ - void SetUnicode(bool a_bIsUtf8 = true) { - if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8; - } - - /** Get the storage format of the INI data. */ - bool IsUnicode() const { return m_bStoreIsUtf8; } - - /** Should multiple identical keys be permitted in the file. If set to false - then the last value encountered will be used as the value of the key. - If set to true, then all values will be available to be queried. For - example, with the following input: - - <pre> - [section] - test=value1 - test=value2 - </pre> - - Then with SetMultiKey(true), both of the values "value1" and "value2" - will be returned for the key test. If SetMultiKey(false) is used, then - the value for "test" will only be "value2". This value may be changed - at any time. - - \param a_bAllowMultiKey Allow multi-keys in the source? - */ - void SetMultiKey(bool a_bAllowMultiKey = true) { - m_bAllowMultiKey = a_bAllowMultiKey; - } - - /** Get the storage format of the INI data. */ - bool IsMultiKey() const { return m_bAllowMultiKey; } - - /** Should data values be permitted to span multiple lines in the file. If - set to false then the multi-line construct <<<TAG as a value will be - returned as is instead of loading the data. This value may be changed - at any time. - - \param a_bAllowMultiLine Allow multi-line values in the source? - */ - void SetMultiLine(bool a_bAllowMultiLine = true) { - m_bAllowMultiLine = a_bAllowMultiLine; - } - - /** Query the status of multi-line data */ - bool IsMultiLine() const { return m_bAllowMultiLine; } - - /*-----------------------------------------------------------------------*/ - /** @} - @{ @name Loading INI Data */ - - /** Load an INI file from disk into memory - - @param a_pszFile Path of the file to be loaded. This will be passed - to fopen() and so must be a valid path for the - current platform. - - @return SI_Error See error definitions - */ - SI_Error LoadFile( - const char * a_pszFile - ); - -#ifdef SI_HAS_WIDE_FILE - /** Load an INI file from disk into memory - - @param a_pwszFile Path of the file to be loaded in UTF-16. - - @return SI_Error See error definitions - */ - SI_Error LoadFile( - const SI_WCHAR_T * a_pwszFile - ); -#endif // SI_HAS_WIDE_FILE - - /** Load the file from a file pointer. - - @param a_fpFile Valid file pointer to read the file data from. The - file will be read until end of file. - - @return SI_Error See error definitions - */ - SI_Error LoadFile( - FILE * a_fpFile - ); - -#ifdef SI_SUPPORT_IOSTREAMS - /** Load INI file data from an istream. - - @param a_istream Stream to read from - - @return SI_Error See error definitions - */ - SI_Error Load( - std::istream & a_istream - ); -#endif // SI_SUPPORT_IOSTREAMS - - /** Load INI file data direct from a std::string - - @param a_strData Data to be loaded - - @return SI_Error See error definitions - */ - SI_Error Load(const std::string & a_strData) { - return Load(a_strData.c_str(), a_strData.size()); - } - - /** Load INI file data direct from memory - - @param a_pData Data to be loaded - @param a_uDataLen Length of the data in bytes - - @return SI_Error See error definitions - */ - SI_Error Load( - const char * a_pData, - size_t a_uDataLen - ); - - /*-----------------------------------------------------------------------*/ - /** @} - @{ @name Saving INI Data */ - - /** Save an INI file from memory to disk - - @param a_pszFile Path of the file to be saved. This will be passed - to fopen() and so must be a valid path for the - current platform. - - @param a_bAddSignature Prepend the UTF-8 BOM if the output data is - in UTF-8 format. If it is not UTF-8 then - this parameter is ignored. - - @return SI_Error See error definitions - */ - SI_Error SaveFile( - const char * a_pszFile, - bool a_bAddSignature = true - ) const; - -#ifdef SI_HAS_WIDE_FILE - /** Save an INI file from memory to disk - - @param a_pwszFile Path of the file to be saved in UTF-16. - - @param a_bAddSignature Prepend the UTF-8 BOM if the output data is - in UTF-8 format. If it is not UTF-8 then - this parameter is ignored. - - @return SI_Error See error definitions - */ - SI_Error SaveFile( - const SI_WCHAR_T * a_pwszFile, - bool a_bAddSignature = true - ) const; -#endif // _WIN32 - - /** Save the INI data to a file. See Save() for details. - - @param a_pFile Handle to a file. File should be opened for - binary output. - - @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in - UTF-8 format. If it is not UTF-8 then this value is - ignored. Do not set this to true if anything has - already been written to the file. - - @return SI_Error See error definitions - */ - SI_Error SaveFile( - FILE * a_pFile, - bool a_bAddSignature = false - ) const; - - /** Save the INI data. The data will be written to the output device - in a format appropriate to the current data, selected by: - - <table> - <tr><th>SI_CHAR <th>FORMAT - <tr><td>char <td>same format as when loaded (MBCS or UTF-8) - <tr><td>wchar_t <td>UTF-8 - <tr><td>other <td>UTF-8 - </table> - - Note that comments from the original data is preserved as per the - documentation on comments. The order of the sections and values - from the original file will be preserved. - - Any data prepended or appended to the output device must use the the - same format (MBCS or UTF-8). You may use the GetConverter() method to - convert text to the correct format regardless of the output format - being used by SimpleIni. - - To add a BOM to UTF-8 data, write it out manually at the very beginning - like is done in SaveFile when a_bUseBOM is true. - - @param a_oOutput Output writer to write the data to. - - @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in - UTF-8 format. If it is not UTF-8 then this value is - ignored. Do not set this to true if anything has - already been written to the OutputWriter. - - @return SI_Error See error definitions - */ - SI_Error Save( - OutputWriter & a_oOutput, - bool a_bAddSignature = false - ) const; - -#ifdef SI_SUPPORT_IOSTREAMS - /** Save the INI data to an ostream. See Save() for details. - - @param a_ostream String to have the INI data appended to. - - @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in - UTF-8 format. If it is not UTF-8 then this value is - ignored. Do not set this to true if anything has - already been written to the stream. - - @return SI_Error See error definitions - */ - SI_Error Save( - std::ostream & a_ostream, - bool a_bAddSignature = false - ) const - { - StreamWriter writer(a_ostream); - return Save(writer, a_bAddSignature); - } -#endif // SI_SUPPORT_IOSTREAMS - - /** Append the INI data to a string. See Save() for details. - - @param a_sBuffer String to have the INI data appended to. - - @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in - UTF-8 format. If it is not UTF-8 then this value is - ignored. Do not set this to true if anything has - already been written to the string. - - @return SI_Error See error definitions - */ - SI_Error Save( - std::string & a_sBuffer, - bool a_bAddSignature = false - ) const - { - StringWriter writer(a_sBuffer); - return Save(writer, a_bAddSignature); - } - - /*-----------------------------------------------------------------------*/ - /** @} - @{ @name Accessing INI Data */ - - /** Retrieve all section names. The list is returned as an STL vector of - names and can be iterated or searched as necessary. Note that the - sort order of the returned strings is NOT DEFINED. You can sort - the names into the load order if desired. Search this file for ".sort" - for an example. - - NOTE! This structure contains only pointers to strings. The actual - string data is stored in memory owned by CSimpleIni. Ensure that the - CSimpleIni object is not destroyed or Reset() while these pointers - are in use! - - @param a_names Vector that will receive all of the section - names. See note above! - */ - void GetAllSections( - TNamesDepend & a_names - ) const; - - /** Retrieve all unique key names in a section. The sort order of the - returned strings is NOT DEFINED. You can sort the names into the load - order if desired. Search this file for ".sort" for an example. Only - unique key names are returned. - - NOTE! This structure contains only pointers to strings. The actual - string data is stored in memory owned by CSimpleIni. Ensure that the - CSimpleIni object is not destroyed or Reset() while these strings - are in use! - - @param a_pSection Section to request data for - @param a_names List that will receive all of the key - names. See note above! - - @return true Section was found. - @return false Matching section was not found. - */ - bool GetAllKeys( - const SI_CHAR * a_pSection, - TNamesDepend & a_names - ) const; - - /** Retrieve all values for a specific key. This method can be used when - multiple keys are both enabled and disabled. Note that the sort order - of the returned strings is NOT DEFINED. You can sort the names into - the load order if desired. Search this file for ".sort" for an example. - - NOTE! The returned values are pointers to string data stored in memory - owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed - or Reset while you are using this pointer! - - @param a_pSection Section to search - @param a_pKey Key to search for - @param a_values List to return if the key is not found - - @return true Key was found. - @return false Matching section/key was not found. - */ - bool GetAllValues( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - TNamesDepend & a_values - ) const; - - /** Query the number of keys in a specific section. Note that if multiple - keys are enabled, then this value may be different to the number of - keys returned by GetAllKeys. - - @param a_pSection Section to request data for - - @return -1 Section does not exist in the file - @return >=0 Number of keys in the section - */ - int GetSectionSize( - const SI_CHAR * a_pSection - ) const; - - /** Retrieve all key and value pairs for a section. The data is returned - as a pointer to an STL map and can be iterated or searched as - desired. Note that multiple entries for the same key may exist when - multiple keys have been enabled. - - NOTE! This structure contains only pointers to strings. The actual - string data is stored in memory owned by CSimpleIni. Ensure that the - CSimpleIni object is not destroyed or Reset() while these strings - are in use! - - @param a_pSection Name of the section to return - @return boolean Was a section matching the supplied - name found. - */ - const TKeyVal * GetSection( - const SI_CHAR * a_pSection - ) const; - - /** Retrieve the value for a specific key. If multiple keys are enabled - (see SetMultiKey) then only the first value associated with that key - will be returned, see GetAllValues for getting all values with multikey. - - NOTE! The returned value is a pointer to string data stored in memory - owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed - or Reset while you are using this pointer! - - @param a_pSection Section to search - @param a_pKey Key to search for - @param a_pDefault Value to return if the key is not found - @param a_pHasMultiple Optionally receive notification of if there are - multiple entries for this key. - - @return a_pDefault Key was not found in the section - @return other Value of the key - */ - const SI_CHAR * GetValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - const SI_CHAR * a_pDefault = NULL, - bool * a_pHasMultiple = NULL - ) const; - - /** Retrieve a numeric value for a specific key. If multiple keys are enabled - (see SetMultiKey) then only the first value associated with that key - will be returned, see GetAllValues for getting all values with multikey. - - @param a_pSection Section to search - @param a_pKey Key to search for - @param a_nDefault Value to return if the key is not found - @param a_pHasMultiple Optionally receive notification of if there are - multiple entries for this key. - - @return a_nDefault Key was not found in the section - @return other Value of the key - */ - long GetLongValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - long a_nDefault = 0, - bool * a_pHasMultiple = NULL - ) const; - - /** Retrieve a boolean value for a specific key. If multiple keys are enabled - (see SetMultiKey) then only the first value associated with that key - will be returned, see GetAllValues for getting all values with multikey. - - Strings starting with "t", "y", "on" or "1" are returned as logically true. - Strings starting with "f", "n", "of" or "0" are returned as logically false. - For all other values the default is returned. Character comparisons are - case-insensitive. - - @param a_pSection Section to search - @param a_pKey Key to search for - @param a_bDefault Value to return if the key is not found - @param a_pHasMultiple Optionally receive notification of if there are - multiple entries for this key. - - @return a_nDefault Key was not found in the section - @return other Value of the key - */ - bool GetBoolValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - bool a_bDefault = false, - bool * a_pHasMultiple = NULL - ) const; - - /** Add or update a section or value. This will always insert - when multiple keys are enabled. - - @param a_pSection Section to add or update - @param a_pKey Key to add or update. Set to NULL to - create an empty section. - @param a_pValue Value to set. Set to NULL to create an - empty section. - @param a_pComment Comment to be associated with the section or the - key. If a_pKey is NULL then it will be associated - with the section, otherwise the key. Note that a - comment may be set ONLY when the section or key is - first created (i.e. when this function returns the - value SI_INSERTED). If you wish to create a section - with a comment then you need to create the section - separately to the key. The comment string must be - in full comment form already (have a comment - character starting every line). - @param a_bForceReplace Should all existing values in a multi-key INI - file be replaced with this entry. This option has - no effect if not using multi-key files. The - difference between Delete/SetValue and SetValue - with a_bForceReplace = true, is that the load - order and comment will be preserved this way. - - @return SI_Error See error definitions - @return SI_UPDATED Value was updated - @return SI_INSERTED Value was inserted - */ - SI_Error SetValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - const SI_CHAR * a_pValue, - const SI_CHAR * a_pComment = NULL, - bool a_bForceReplace = false - ) - { - return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true); - } - - /** Add or update a numeric value. This will always insert - when multiple keys are enabled. - - @param a_pSection Section to add or update - @param a_pKey Key to add or update. - @param a_nValue Value to set. - @param a_pComment Comment to be associated with the key. See the - notes on SetValue() for comments. - @param a_bUseHex By default the value will be written to the file - in decimal format. Set this to true to write it - as hexadecimal. - @param a_bForceReplace Should all existing values in a multi-key INI - file be replaced with this entry. This option has - no effect if not using multi-key files. The - difference between Delete/SetLongValue and - SetLongValue with a_bForceReplace = true, is that - the load order and comment will be preserved this - way. - - @return SI_Error See error definitions - @return SI_UPDATED Value was updated - @return SI_INSERTED Value was inserted - */ - SI_Error SetLongValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - long a_nValue, - const SI_CHAR * a_pComment = NULL, - bool a_bUseHex = false, - bool a_bForceReplace = false - ); - - /** Add or update a boolean value. This will always insert - when multiple keys are enabled. - - @param a_pSection Section to add or update - @param a_pKey Key to add or update. - @param a_bValue Value to set. - @param a_pComment Comment to be associated with the key. See the - notes on SetValue() for comments. - @param a_bForceReplace Should all existing values in a multi-key INI - file be replaced with this entry. This option has - no effect if not using multi-key files. The - difference between Delete/SetBoolValue and - SetBoolValue with a_bForceReplace = true, is that - the load order and comment will be preserved this - way. - - @return SI_Error See error definitions - @return SI_UPDATED Value was updated - @return SI_INSERTED Value was inserted - */ - SI_Error SetBoolValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - bool a_nValue, - const SI_CHAR * a_pComment = NULL, - bool a_bForceReplace = false - ); - - /** Delete an entire section, or a key from a section. Note that the - data returned by GetSection is invalid and must not be used after - anything has been deleted from that section using this method. - Note when multiple keys is enabled, this will delete all keys with - that name; there is no way to selectively delete individual key/values - in this situation. - - @param a_pSection Section to delete key from, or if - a_pKey is NULL, the section to remove. - @param a_pKey Key to remove from the section. Set to - NULL to remove the entire section. - @param a_bRemoveEmpty If the section is empty after this key has - been deleted, should the empty section be - removed? - - @return true Key or section was deleted. - @return false Key or section was not found. - */ - bool Delete( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - bool a_bRemoveEmpty = false - ); - - /*-----------------------------------------------------------------------*/ - /** @} - @{ @name Converter */ - - /** Return a conversion object to convert text to the same encoding - as is used by the Save(), SaveFile() and SaveString() functions. - Use this to prepare the strings that you wish to append or prepend - to the output INI data. - */ - Converter GetConverter() const { - return Converter(m_bStoreIsUtf8); - } - - /*-----------------------------------------------------------------------*/ - /** @} */ - -private: - // copying is not permitted - CSimpleIniTempl(const CSimpleIniTempl &); // disabled - CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled - - /** Parse the data looking for a file comment and store it if found. - */ - SI_Error FindFileComment( - SI_CHAR *& a_pData, - bool a_bCopyStrings - ); - - /** Parse the data looking for the next valid entry. The memory pointed to - by a_pData is modified by inserting NULL characters. The pointer is - updated to the current location in the block of text. - */ - bool FindEntry( - SI_CHAR *& a_pData, - const SI_CHAR *& a_pSection, - const SI_CHAR *& a_pKey, - const SI_CHAR *& a_pVal, - const SI_CHAR *& a_pComment - ) const; - - /** Add the section/key/value to our data. - - @param a_pSection Section name. Sections will be created if they - don't already exist. - @param a_pKey Key name. May be NULL to create an empty section. - Existing entries will be updated. New entries will - be created. - @param a_pValue Value for the key. - @param a_pComment Comment to be associated with the section or the - key. If a_pKey is NULL then it will be associated - with the section, otherwise the key. This must be - a string in full comment form already (have a - comment character starting every line). - @param a_bForceReplace Should all existing values in a multi-key INI - file be replaced with this entry. This option has - no effect if not using multi-key files. The - difference between Delete/AddEntry and AddEntry - with a_bForceReplace = true, is that the load - order and comment will be preserved this way. - @param a_bCopyStrings Should copies of the strings be made or not. - If false then the pointers will be used as is. - */ - SI_Error AddEntry( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - const SI_CHAR * a_pValue, - const SI_CHAR * a_pComment, - bool a_bForceReplace, - bool a_bCopyStrings - ); - - /** Is the supplied character a whitespace character? */ - inline bool IsSpace(SI_CHAR ch) const { - return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); - } - - /** Does the supplied character start a comment line? */ - inline bool IsComment(SI_CHAR ch) const { - return (ch == ';' || ch == '#'); - } - - - /** Skip over a newline character (or characters) for either DOS or UNIX */ - inline void SkipNewLine(SI_CHAR *& a_pData) const { - a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1; - } - - /** Make a copy of the supplied string, replacing the original pointer */ - SI_Error CopyString(const SI_CHAR *& a_pString); - - /** Delete a string from the copied strings buffer if necessary */ - void DeleteString(const SI_CHAR * a_pString); - - /** Internal use of our string comparison function */ - bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const { - const static SI_STRLESS isLess = SI_STRLESS(); - return isLess(a_pLeft, a_pRight); - } - - bool IsMultiLineTag(const SI_CHAR * a_pData) const; - bool IsMultiLineData(const SI_CHAR * a_pData) const; - bool LoadMultiLineText( - SI_CHAR *& a_pData, - const SI_CHAR *& a_pVal, - const SI_CHAR * a_pTagName, - bool a_bAllowBlankLinesInComment = false - ) const; - bool IsNewLineChar(SI_CHAR a_c) const; - - bool OutputMultiLineText( - OutputWriter & a_oOutput, - Converter & a_oConverter, - const SI_CHAR * a_pText - ) const; - -private: - /** Copy of the INI file data in our character format. This will be - modified when parsed to have NULL characters added after all - interesting string entries. All of the string pointers to sections, - keys and values point into this block of memory. - */ - SI_CHAR * m_pData; - - /** Length of the data that we have stored. Used when deleting strings - to determine if the string is stored here or in the allocated string - buffer. - */ - size_t m_uDataLen; - - /** File comment for this data, if one exists. */ - const SI_CHAR * m_pFileComment; - - /** Parsed INI data. Section -> (Key -> Value). */ - TSection m_data; - - /** This vector stores allocated memory for copies of strings that have - been supplied after the file load. It will be empty unless SetValue() - has been called. - */ - TNamesDepend m_strings; - - /** Is the format of our datafile UTF-8 or MBCS? */ - bool m_bStoreIsUtf8; - - /** Are multiple values permitted for the same key? */ - bool m_bAllowMultiKey; - - /** Are data values permitted to span multiple lines? */ - bool m_bAllowMultiLine; - - /** Next order value, used to ensure sections and keys are output in the - same order that they are loaded/added. - */ - int m_nOrder; -}; - -// --------------------------------------------------------------------------- -// IMPLEMENTATION -// --------------------------------------------------------------------------- - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl( - bool a_bIsUtf8, - bool a_bAllowMultiKey, - bool a_bAllowMultiLine - ) - : m_pData(0) - , m_uDataLen(0) - , m_pFileComment(NULL) - , m_bStoreIsUtf8(a_bIsUtf8) - , m_bAllowMultiKey(a_bAllowMultiKey) - , m_bAllowMultiLine(a_bAllowMultiLine) - , m_nOrder(0) -{ } - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::~CSimpleIniTempl() -{ - Reset(); -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -void -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Reset() -{ - // remove all data - delete[] m_pData; - m_pData = NULL; - m_uDataLen = 0; - m_pFileComment = NULL; - if (!m_data.empty()) { - m_data.erase(m_data.begin(), m_data.end()); - } - - // remove all strings - if (!m_strings.empty()) { - typename TNamesDepend::iterator i = m_strings.begin(); - for (; i != m_strings.end(); ++i) { - delete[] const_cast<SI_CHAR*>(i->pItem); - } - m_strings.erase(m_strings.begin(), m_strings.end()); - } -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile( - const char * a_pszFile - ) -{ - FILE * fp = NULL; -#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE - fopen_s(&fp, a_pszFile, "rb"); -#else // !__STDC_WANT_SECURE_LIB__ - fp = fopen(a_pszFile, "rb"); -#endif // __STDC_WANT_SECURE_LIB__ - if (!fp) { - return SI_FILE; - } - SI_Error rc = LoadFile(fp); - fclose(fp); - return rc; -} - -#ifdef SI_HAS_WIDE_FILE -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile( - const SI_WCHAR_T * a_pwszFile - ) -{ -#ifdef _WIN32 - FILE * fp = NULL; -#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE - _wfopen_s(&fp, a_pwszFile, L"rb"); -#else // !__STDC_WANT_SECURE_LIB__ - fp = _wfopen(a_pwszFile, L"rb"); -#endif // __STDC_WANT_SECURE_LIB__ - if (!fp) return SI_FILE; - SI_Error rc = LoadFile(fp); - fclose(fp); - return rc; -#else // !_WIN32 (therefore SI_CONVERT_ICU) - char szFile[256]; - u_austrncpy(szFile, a_pwszFile, sizeof(szFile)); - return LoadFile(szFile); -#endif // _WIN32 -} -#endif // SI_HAS_WIDE_FILE - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile( - FILE * a_fpFile - ) -{ - // load the raw file data - int retval = fseek(a_fpFile, 0, SEEK_END); - if (retval != 0) { - return SI_FILE; - } - long lSize = ftell(a_fpFile); - if (lSize < 0) { - return SI_FILE; - } - if (lSize == 0) { - return SI_OK; - } - char * pData = new char[lSize]; - if (!pData) { - return SI_NOMEM; - } - fseek(a_fpFile, 0, SEEK_SET); - size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile); - if (uRead != (size_t) lSize) { - delete[] pData; - return SI_FILE; - } - - // convert the raw data to unicode - SI_Error rc = Load(pData, uRead); - delete[] pData; - return rc; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Load( - const char * a_pData, - size_t a_uDataLen - ) -{ - SI_CONVERTER converter(m_bStoreIsUtf8); - - if (a_uDataLen == 0) { - return SI_OK; - } - - // consume the UTF-8 BOM if it exists - if (m_bStoreIsUtf8 && a_uDataLen >= 3) { - if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) { - a_pData += 3; - a_uDataLen -= 3; - } - } - - // determine the length of the converted data - size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen); - if (uLen == (size_t)(-1)) { - return SI_FAIL; - } - - // allocate memory for the data, ensure that there is a NULL - // terminator wherever the converted data ends - SI_CHAR * pData = new SI_CHAR[uLen+1]; - if (!pData) { - return SI_NOMEM; - } - memset(pData, 0, sizeof(SI_CHAR)*(uLen+1)); - - // convert the data - if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) { - delete[] pData; - return SI_FAIL; - } - - // parse it - const static SI_CHAR empty = 0; - SI_CHAR * pWork = pData; - const SI_CHAR * pSection = ∅ - const SI_CHAR * pItem = NULL; - const SI_CHAR * pVal = NULL; - const SI_CHAR * pComment = NULL; - - // We copy the strings if we are loading data into this class when we - // already have stored some. - bool bCopyStrings = (m_pData != NULL); - - // find a file comment if it exists, this is a comment that starts at the - // beginning of the file and continues until the first blank line. - SI_Error rc = FindFileComment(pWork, bCopyStrings); - if (rc < 0) return rc; - - // add every entry in the file to the data table - while (FindEntry(pWork, pSection, pItem, pVal, pComment)) { - rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings); - if (rc < 0) return rc; - } - - // store these strings if we didn't copy them - if (bCopyStrings) { - delete[] pData; - } - else { - m_pData = pData; - m_uDataLen = uLen+1; - } - - return SI_OK; -} - -#ifdef SI_SUPPORT_IOSTREAMS -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Load( - std::istream & a_istream - ) -{ - std::string strData; - char szBuf[512]; - do { - a_istream.get(szBuf, sizeof(szBuf), '\0'); - strData.append(szBuf); - } - while (a_istream.good()); - return Load(strData); -} -#endif // SI_SUPPORT_IOSTREAMS - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindFileComment( - SI_CHAR *& a_pData, - bool a_bCopyStrings - ) -{ - // there can only be a single file comment - if (m_pFileComment) { - return SI_OK; - } - - // Load the file comment as multi-line text, this will modify all of - // the newline characters to be single \n chars - if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) { - return SI_OK; - } - - // copy the string if necessary - if (a_bCopyStrings) { - SI_Error rc = CopyString(m_pFileComment); - if (rc < 0) return rc; - } - - return SI_OK; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -bool -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry( - SI_CHAR *& a_pData, - const SI_CHAR *& a_pSection, - const SI_CHAR *& a_pKey, - const SI_CHAR *& a_pVal, - const SI_CHAR *& a_pComment - ) const -{ - a_pComment = NULL; - - SI_CHAR * pTrail = NULL; - while (*a_pData) { - // skip spaces and empty lines - while (*a_pData && IsSpace(*a_pData)) { - ++a_pData; - } - if (!*a_pData) { - break; - } - - // skip processing of comment lines but keep a pointer to - // the start of the comment. - if (IsComment(*a_pData)) { - LoadMultiLineText(a_pData, a_pComment, NULL, true); - continue; - } - - // process section names - if (*a_pData == '[') { - // skip leading spaces - ++a_pData; - while (*a_pData && IsSpace(*a_pData)) { - ++a_pData; - } - - // find the end of the section name (it may contain spaces) - // and convert it to lowercase as necessary - a_pSection = a_pData; - while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) { - ++a_pData; - } - - // if it's an invalid line, just skip it - if (*a_pData != ']') { - continue; - } - - // remove trailing spaces from the section - pTrail = a_pData - 1; - while (pTrail >= a_pSection && IsSpace(*pTrail)) { - --pTrail; - } - ++pTrail; - *pTrail = 0; - - // skip to the end of the line - ++a_pData; // safe as checked that it == ']' above - while (*a_pData && !IsNewLineChar(*a_pData)) { - ++a_pData; - } - - a_pKey = NULL; - a_pVal = NULL; - return true; - } - - // find the end of the key name (it may contain spaces) - // and convert it to lowercase as necessary - a_pKey = a_pData; - while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) { - ++a_pData; - } - - // if it's an invalid line, just skip it - if (*a_pData != '=') { - continue; - } - - // empty keys are invalid - if (a_pKey == a_pData) { - while (*a_pData && !IsNewLineChar(*a_pData)) { - ++a_pData; - } - continue; - } - - // remove trailing spaces from the key - pTrail = a_pData - 1; - while (pTrail >= a_pKey && IsSpace(*pTrail)) { - --pTrail; - } - ++pTrail; - *pTrail = 0; - - // skip leading whitespace on the value - ++a_pData; // safe as checked that it == '=' above - while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) { - ++a_pData; - } - - // find the end of the value which is the end of this line - a_pVal = a_pData; - while (*a_pData && !IsNewLineChar(*a_pData)) { - ++a_pData; - } - - // remove trailing spaces from the value - pTrail = a_pData - 1; - if (*a_pData) { // prepare for the next round - SkipNewLine(a_pData); - } - while (pTrail >= a_pVal && IsSpace(*pTrail)) { - --pTrail; - } - ++pTrail; - *pTrail = 0; - - // check for multi-line entries - if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) { - // skip the "<<<" to get the tag that will end the multiline - const SI_CHAR * pTagName = a_pVal + 3; - return LoadMultiLineText(a_pData, a_pVal, pTagName); - } - - // return the standard entry - return true; - } - - return false; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -bool -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineTag( - const SI_CHAR * a_pVal - ) const -{ - // check for the "<<<" prefix for a multi-line entry - if (*a_pVal++ != '<') return false; - if (*a_pVal++ != '<') return false; - if (*a_pVal++ != '<') return false; - return true; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -bool -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData( - const SI_CHAR * a_pData - ) const -{ - // data is multi-line if it has any of the following features: - // * whitespace prefix - // * embedded newlines - // * whitespace suffix - - // empty string - if (!*a_pData) { - return false; - } - - // check for prefix - if (IsSpace(*a_pData)) { - return true; - } - - // embedded newlines - while (*a_pData) { - if (IsNewLineChar(*a_pData)) { - return true; - } - ++a_pData; - } - - // check for suffix - if (IsSpace(*--a_pData)) { - return true; - } - - return false; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -bool -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar( - SI_CHAR a_c - ) const -{ - return (a_c == '\n' || a_c == '\r'); -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -bool -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText( - SI_CHAR *& a_pData, - const SI_CHAR *& a_pVal, - const SI_CHAR * a_pTagName, - bool a_bAllowBlankLinesInComment - ) const -{ - // we modify this data to strip all newlines down to a single '\n' - // character. This means that on Windows we need to strip out some - // characters which will make the data shorter. - // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become - // LINE1-LINE1\nLINE2-LINE2\0 - // The pDataLine entry is the pointer to the location in memory that - // the current line needs to start to run following the existing one. - // This may be the same as pCurrLine in which case no move is needed. - SI_CHAR * pDataLine = a_pData; - SI_CHAR * pCurrLine; - - // value starts at the current line - a_pVal = a_pData; - - // find the end tag. This tag must start in column 1 and be - // followed by a newline. No whitespace removal is done while - // searching for this tag. - SI_CHAR cEndOfLineChar = *a_pData; - for(;;) { - // if we are loading comments then we need a comment character as - // the first character on every line - if (!a_pTagName && !IsComment(*a_pData)) { - // if we aren't allowing blank lines then we're done - if (!a_bAllowBlankLinesInComment) { - break; - } - - // if we are allowing blank lines then we only include them - // in this comment if another comment follows, so read ahead - // to find out. - SI_CHAR * pCurr = a_pData; - int nNewLines = 0; - while (IsSpace(*pCurr)) { - if (IsNewLineChar(*pCurr)) { - ++nNewLines; - SkipNewLine(pCurr); - } - else { - ++pCurr; - } - } - - // we have a comment, add the blank lines to the output - // and continue processing from here - if (IsComment(*pCurr)) { - for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n'; - a_pData = pCurr; - continue; - } - - // the comment ends here - break; - } - - // find the end of this line - pCurrLine = a_pData; - while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData; - - // move this line down to the location that it should be if necessary - if (pDataLine < pCurrLine) { - size_t nLen = (size_t) (a_pData - pCurrLine); - memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR)); - pDataLine[nLen] = '\0'; - } - - // end the line with a NULL - cEndOfLineChar = *a_pData; - *a_pData = 0; - - // if are looking for a tag then do the check now. This is done before - // checking for end of the data, so that if we have the tag at the end - // of the data then the tag is removed correctly. - if (a_pTagName && - (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine))) - { - break; - } - - // if we are at the end of the data then we just automatically end - // this entry and return the current data. - if (!cEndOfLineChar) { - return true; - } - - // otherwise we need to process this newline to ensure that it consists - // of just a single \n character. - pDataLine += (a_pData - pCurrLine); - *a_pData = cEndOfLineChar; - SkipNewLine(a_pData); - *pDataLine++ = '\n'; - } - - // if we didn't find a comment at all then return false - if (a_pVal == a_pData) { - a_pVal = NULL; - return false; - } - - // the data (which ends at the end of the last line) needs to be - // null-terminated BEFORE before the newline character(s). If the - // user wants a new line in the multi-line data then they need to - // add an empty line before the tag. - *--pDataLine = '\0'; - - // if looking for a tag and if we aren't at the end of the data, - // then move a_pData to the start of the next line. - if (a_pTagName && cEndOfLineChar) { - SI_ASSERT(IsNewLineChar(cEndOfLineChar)); - *a_pData = cEndOfLineChar; - SkipNewLine(a_pData); - } - - return true; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString( - const SI_CHAR *& a_pString - ) -{ - size_t uLen = 0; - if (sizeof(SI_CHAR) == sizeof(char)) { - uLen = strlen((const char *)a_pString); - } - else if (sizeof(SI_CHAR) == sizeof(wchar_t)) { - uLen = wcslen((const wchar_t *)a_pString); - } - else { - for ( ; a_pString[uLen]; ++uLen) /*loop*/ ; - } - ++uLen; // NULL character - SI_CHAR * pCopy = new SI_CHAR[uLen]; - if (!pCopy) { - return SI_NOMEM; - } - memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen); - m_strings.push_back(pCopy); - a_pString = pCopy; - return SI_OK; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - const SI_CHAR * a_pValue, - const SI_CHAR * a_pComment, - bool a_bForceReplace, - bool a_bCopyStrings - ) -{ - SI_Error rc; - bool bInserted = false; - - SI_ASSERT(!a_pComment || IsComment(*a_pComment)); - - // if we are copying strings then make a copy of the comment now - // because we will need it when we add the entry. - if (a_bCopyStrings && a_pComment) { - rc = CopyString(a_pComment); - if (rc < 0) return rc; - } - - // create the section entry if necessary - typename TSection::iterator iSection = m_data.find(a_pSection); - if (iSection == m_data.end()) { - // if the section doesn't exist then we need a copy as the - // string needs to last beyond the end of this function - if (a_bCopyStrings) { - rc = CopyString(a_pSection); - if (rc < 0) return rc; - } - - // only set the comment if this is a section only entry - Entry oSection(a_pSection, ++m_nOrder); - if (a_pComment && (!a_pKey || !a_pValue)) { - oSection.pComment = a_pComment; - } - - typename TSection::value_type oEntry(oSection, TKeyVal()); - typedef typename TSection::iterator SectionIterator; - std::pair<SectionIterator,bool> i = m_data.insert(oEntry); - iSection = i.first; - bInserted = true; - } - if (!a_pKey || !a_pValue) { - // section only entries are specified with pItem and pVal as NULL - return bInserted ? SI_INSERTED : SI_UPDATED; - } - - // check for existence of the key - TKeyVal & keyval = iSection->second; - typename TKeyVal::iterator iKey = keyval.find(a_pKey); - - // remove all existing entries but save the load order and - // comment of the first entry - int nLoadOrder = ++m_nOrder; - if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) { - const SI_CHAR * pComment = NULL; - while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) { - if (iKey->first.nOrder < nLoadOrder) { - nLoadOrder = iKey->first.nOrder; - pComment = iKey->first.pComment; - } - ++iKey; - } - if (pComment) { - DeleteString(a_pComment); - a_pComment = pComment; - CopyString(a_pComment); - } - Delete(a_pSection, a_pKey); - iKey = keyval.end(); - } - - // make string copies if necessary - bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace; - if (a_bCopyStrings) { - if (bForceCreateNewKey || iKey == keyval.end()) { - // if the key doesn't exist then we need a copy as the - // string needs to last beyond the end of this function - // because we will be inserting the key next - rc = CopyString(a_pKey); - if (rc < 0) return rc; - } - - // we always need a copy of the value - rc = CopyString(a_pValue); - if (rc < 0) return rc; - } - - // create the key entry - if (iKey == keyval.end() || bForceCreateNewKey) { - Entry oKey(a_pKey, nLoadOrder); - if (a_pComment) { - oKey.pComment = a_pComment; - } - typename TKeyVal::value_type oEntry(oKey, NULL); - iKey = keyval.insert(oEntry); - bInserted = true; - } - iKey->second = a_pValue; - return bInserted ? SI_INSERTED : SI_UPDATED; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -const SI_CHAR * -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - const SI_CHAR * a_pDefault, - bool * a_pHasMultiple - ) const -{ - if (a_pHasMultiple) { - *a_pHasMultiple = false; - } - if (!a_pSection || !a_pKey) { - return a_pDefault; - } - typename TSection::const_iterator iSection = m_data.find(a_pSection); - if (iSection == m_data.end()) { - return a_pDefault; - } - typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey); - if (iKeyVal == iSection->second.end()) { - return a_pDefault; - } - - // check for multiple entries with the same key - if (m_bAllowMultiKey && a_pHasMultiple) { - typename TKeyVal::const_iterator iTemp = iKeyVal; - if (++iTemp != iSection->second.end()) { - if (!IsLess(a_pKey, iTemp->first.pItem)) { - *a_pHasMultiple = true; - } - } - } - - return iKeyVal->second; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -long -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetLongValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - long a_nDefault, - bool * a_pHasMultiple - ) const -{ - // return the default if we don't have a value - const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); - if (!pszValue || !*pszValue) return a_nDefault; - - // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII - char szValue[64] = { 0 }; - SI_CONVERTER c(m_bStoreIsUtf8); - if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) { - return a_nDefault; - } - - // handle the value as hex if prefaced with "0x" - long nValue = a_nDefault; - char * pszSuffix = szValue; - if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) { - if (!szValue[2]) return a_nDefault; - nValue = strtol(&szValue[2], &pszSuffix, 16); - } - else { - nValue = strtol(szValue, &pszSuffix, 10); - } - - // any invalid strings will return the default value - if (*pszSuffix) { - return a_nDefault; - } - - return nValue; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - long a_nValue, - const SI_CHAR * a_pComment, - bool a_bUseHex, - bool a_bForceReplace - ) -{ - // use SetValue to create sections - if (!a_pSection || !a_pKey) return SI_FAIL; - - // convert to an ASCII string - char szInput[64]; - sprintf(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue); - - // convert to output text - SI_CHAR szOutput[64]; - SI_CONVERTER c(m_bStoreIsUtf8); - c.ConvertFromStore(szInput, strlen(szInput) + 1, - szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); - - // actually add it - return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -bool -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetBoolValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - bool a_bDefault, - bool * a_pHasMultiple - ) const -{ - // return the default if we don't have a value - const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); - if (!pszValue || !*pszValue) return a_bDefault; - - // we only look at the minimum number of characters - switch (pszValue[0]) { - case 't': case 'T': // true - case 'y': case 'Y': // yes - case '1': // 1 (one) - return true; - - case 'f': case 'F': // false - case 'n': case 'N': // no - case '0': // 0 (zero) - return false; - - case 'o': case 'O': - if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on - if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off - break; - } - - // no recognized value, return the default - return a_bDefault; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetBoolValue( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - bool a_bValue, - const SI_CHAR * a_pComment, - bool a_bForceReplace - ) -{ - // use SetValue to create sections - if (!a_pSection || !a_pKey) return SI_FAIL; - - // convert to an ASCII string - const char * pszInput = a_bValue ? "true" : "false"; - - // convert to output text - SI_CHAR szOutput[64]; - SI_CONVERTER c(m_bStoreIsUtf8); - c.ConvertFromStore(pszInput, strlen(pszInput) + 1, - szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); - - // actually add it - return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -bool -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - TNamesDepend & a_values - ) const -{ - a_values.clear(); - - if (!a_pSection || !a_pKey) { - return false; - } - typename TSection::const_iterator iSection = m_data.find(a_pSection); - if (iSection == m_data.end()) { - return false; - } - typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey); - if (iKeyVal == iSection->second.end()) { - return false; - } - - // insert all values for this key - a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder)); - if (m_bAllowMultiKey) { - ++iKeyVal; - while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) { - a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder)); - ++iKeyVal; - } - } - - return true; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -int -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSectionSize( - const SI_CHAR * a_pSection - ) const -{ - if (!a_pSection) { - return -1; - } - - typename TSection::const_iterator iSection = m_data.find(a_pSection); - if (iSection == m_data.end()) { - return -1; - } - const TKeyVal & section = iSection->second; - - // if multi-key isn't permitted then the section size is - // the number of keys that we have. - if (!m_bAllowMultiKey || section.empty()) { - return (int) section.size(); - } - - // otherwise we need to count them - int nCount = 0; - const SI_CHAR * pLastKey = NULL; - typename TKeyVal::const_iterator iKeyVal = section.begin(); - for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) { - if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) { - ++nCount; - pLastKey = iKeyVal->first.pItem; - } - } - return nCount; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -const typename CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::TKeyVal * -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSection( - const SI_CHAR * a_pSection - ) const -{ - if (a_pSection) { - typename TSection::const_iterator i = m_data.find(a_pSection); - if (i != m_data.end()) { - return &(i->second); - } - } - return 0; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -void -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections( - TNamesDepend & a_names - ) const -{ - a_names.clear(); - typename TSection::const_iterator i = m_data.begin(); - for (int n = 0; i != m_data.end(); ++i, ++n ) { - a_names.push_back(i->first); - } -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -bool -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllKeys( - const SI_CHAR * a_pSection, - TNamesDepend & a_names - ) const -{ - a_names.clear(); - - if (!a_pSection) { - return false; - } - - typename TSection::const_iterator iSection = m_data.find(a_pSection); - if (iSection == m_data.end()) { - return false; - } - - const TKeyVal & section = iSection->second; - const SI_CHAR * pLastKey = NULL; - typename TKeyVal::const_iterator iKeyVal = section.begin(); - for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) { - if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) { - a_names.push_back(iKeyVal->first); - pLastKey = iKeyVal->first.pItem; - } - } - - return true; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile( - const char * a_pszFile, - bool a_bAddSignature - ) const -{ - FILE * fp = NULL; -#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE - fopen_s(&fp, a_pszFile, "wb"); -#else // !__STDC_WANT_SECURE_LIB__ - fp = fopen(a_pszFile, "wb"); -#endif // __STDC_WANT_SECURE_LIB__ - if (!fp) return SI_FILE; - SI_Error rc = SaveFile(fp, a_bAddSignature); - fclose(fp); - return rc; -} - -#ifdef SI_HAS_WIDE_FILE -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile( - const SI_WCHAR_T * a_pwszFile, - bool a_bAddSignature - ) const -{ -#ifdef _WIN32 - FILE * fp = _wfopen(a_pwszFile, L"wb"); - if (!fp) return SI_FILE; - SI_Error rc = SaveFile(fp, a_bAddSignature); - fclose(fp); - return rc; -#else // !_WIN32 (therefore SI_CONVERT_ICU) - char szFile[256]; - u_austrncpy(szFile, a_pwszFile, sizeof(szFile)); - return SaveFile(szFile, a_bAddSignature); -#endif // _WIN32 -} -#endif // SI_HAS_WIDE_FILE - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile( - FILE * a_pFile, - bool a_bAddSignature - ) const -{ - FileWriter writer(a_pFile); - return Save(writer, a_bAddSignature); -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -SI_Error -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save( - OutputWriter & a_oOutput, - bool a_bAddSignature - ) const -{ - Converter convert(m_bStoreIsUtf8); - - // add the UTF-8 signature if it is desired - if (m_bStoreIsUtf8 && a_bAddSignature) { - a_oOutput.Write(SI_UTF8_SIGNATURE); - } - - // get all of the sections sorted in load order - TNamesDepend oSections; - GetAllSections(oSections); -#if defined(_MSC_VER) && _MSC_VER <= 1200 - oSections.sort(); -#elif defined(__BORLANDC__) - oSections.sort(Entry::LoadOrder()); -#else - oSections.sort(typename Entry::LoadOrder()); -#endif - - // write the file comment if we have one - bool bNeedNewLine = false; - if (m_pFileComment) { - if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) { - return SI_FAIL; - } - bNeedNewLine = true; - } - - // iterate through our sections and output the data - typename TNamesDepend::const_iterator iSection = oSections.begin(); - for ( ; iSection != oSections.end(); ++iSection ) { - // write out the comment if there is one - if (iSection->pComment) { - if (bNeedNewLine) { - a_oOutput.Write(SI_NEWLINE_A); - a_oOutput.Write(SI_NEWLINE_A); - } - if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) { - return SI_FAIL; - } - bNeedNewLine = false; - } - - if (bNeedNewLine) { - a_oOutput.Write(SI_NEWLINE_A); - a_oOutput.Write(SI_NEWLINE_A); - bNeedNewLine = false; - } - - // write the section (unless there is no section name) - if (*iSection->pItem) { - if (!convert.ConvertToStore(iSection->pItem)) { - return SI_FAIL; - } - a_oOutput.Write("["); - a_oOutput.Write(convert.Data()); - a_oOutput.Write("]"); - a_oOutput.Write(SI_NEWLINE_A); - } - - // get all of the keys sorted in load order - TNamesDepend oKeys; - GetAllKeys(iSection->pItem, oKeys); -#if defined(_MSC_VER) && _MSC_VER <= 1200 - oKeys.sort(); -#elif defined(__BORLANDC__) - oKeys.sort(Entry::LoadOrder()); -#else - oKeys.sort(typename Entry::LoadOrder()); -#endif - - // write all keys and values - typename TNamesDepend::const_iterator iKey = oKeys.begin(); - for ( ; iKey != oKeys.end(); ++iKey) { - // get all values for this key - TNamesDepend oValues; - GetAllValues(iSection->pItem, iKey->pItem, oValues); - - typename TNamesDepend::const_iterator iValue = oValues.begin(); - for ( ; iValue != oValues.end(); ++iValue) { - // write out the comment if there is one - if (iValue->pComment) { - a_oOutput.Write(SI_NEWLINE_A); - if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) { - return SI_FAIL; - } - } - - // write the key - if (!convert.ConvertToStore(iKey->pItem)) { - return SI_FAIL; - } - a_oOutput.Write(convert.Data()); - - // write the value - if (!convert.ConvertToStore(iValue->pItem)) { - return SI_FAIL; - } - a_oOutput.Write(" = "); - if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) { - // multi-line data needs to be processed specially to ensure - // that we use the correct newline format for the current system - a_oOutput.Write("<<<SI-END-OF-MULTILINE-TEXT" SI_NEWLINE_A); - if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) { - return SI_FAIL; - } - a_oOutput.Write("SI-END-OF-MULTILINE-TEXT"); - } - else { - a_oOutput.Write(convert.Data()); - } - a_oOutput.Write(SI_NEWLINE_A); - } - } - - bNeedNewLine = true; - } - - return SI_OK; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -bool -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::OutputMultiLineText( - OutputWriter & a_oOutput, - Converter & a_oConverter, - const SI_CHAR * a_pText - ) const -{ - const SI_CHAR * pEndOfLine; - SI_CHAR cEndOfLineChar = *a_pText; - while (cEndOfLineChar) { - // find the end of this line - pEndOfLine = a_pText; - for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ; - cEndOfLineChar = *pEndOfLine; - - // temporarily null terminate, convert and output the line - *const_cast<SI_CHAR*>(pEndOfLine) = 0; - if (!a_oConverter.ConvertToStore(a_pText)) { - return false; - } - *const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar; - a_pText += (pEndOfLine - a_pText) + 1; - a_oOutput.Write(a_oConverter.Data()); - a_oOutput.Write(SI_NEWLINE_A); - } - return true; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -bool -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete( - const SI_CHAR * a_pSection, - const SI_CHAR * a_pKey, - bool a_bRemoveEmpty - ) -{ - if (!a_pSection) { - return false; - } - - typename TSection::iterator iSection = m_data.find(a_pSection); - if (iSection == m_data.end()) { - return false; - } - - // remove a single key if we have a keyname - if (a_pKey) { - typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey); - if (iKeyVal == iSection->second.end()) { - return false; - } - - // remove any copied strings and then the key - typename TKeyVal::iterator iDelete; - do { - iDelete = iKeyVal++; - - DeleteString(iDelete->first.pItem); - DeleteString(iDelete->second); - iSection->second.erase(iDelete); - } - while (iKeyVal != iSection->second.end() - && !IsLess(a_pKey, iKeyVal->first.pItem)); - - // done now if the section is not empty or we are not pruning away - // the empty sections. Otherwise let it fall through into the section - // deletion code - if (!a_bRemoveEmpty || !iSection->second.empty()) { - return true; - } - } - else { - // delete all copied strings from this section. The actual - // entries will be removed when the section is removed. - typename TKeyVal::iterator iKeyVal = iSection->second.begin(); - for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) { - DeleteString(iKeyVal->first.pItem); - DeleteString(iKeyVal->second); - } - } - - // delete the section itself - DeleteString(iSection->first.pItem); - m_data.erase(iSection); - - return true; -} - -template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> -void -CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString( - const SI_CHAR * a_pString - ) -{ - // strings may exist either inside the data block, or they will be - // individually allocated and stored in m_strings. We only physically - // delete those stored in m_strings. - if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) { - typename TNamesDepend::iterator i = m_strings.begin(); - for (;i != m_strings.end(); ++i) { - if (a_pString == i->pItem) { - delete[] const_cast<SI_CHAR*>(i->pItem); - m_strings.erase(i); - break; - } - } - } -} - -// --------------------------------------------------------------------------- -// CONVERSION FUNCTIONS -// --------------------------------------------------------------------------- - -// Defines the conversion classes for different libraries. Before including -// SimpleIni.h, set the converter that you wish you use by defining one of the -// following symbols. -// -// SI_CONVERT_GENERIC Use the Unicode reference conversion library in -// the accompanying files ConvertUTF.h/c -// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires -// ICU headers on include path and icuuc.lib -// SI_CONVERT_WIN32 Use the Win32 API functions for conversion. - -#if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU) -# ifdef _WIN32 -# define SI_CONVERT_WIN32 -# else -# define SI_CONVERT_GENERIC -# endif -#endif - -/** - * Generic case-sensitive less than comparison. This class returns numerically - * ordered ASCII case-sensitive text for all possible sizes and types of - * SI_CHAR. - */ -template<class SI_CHAR> -struct SI_GenericCase { - bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { - long cmp; - for ( ;*pLeft && *pRight; ++pLeft, ++pRight) { - cmp = (long) *pLeft - (long) *pRight; - if (cmp != 0) { - return cmp < 0; - } - } - return *pRight != 0; - } -}; - -/** - * Generic ASCII case-insensitive less than comparison. This class returns - * numerically ordered ASCII case-insensitive text for all possible sizes - * and types of SI_CHAR. It is not safe for MBCS text comparison where - * ASCII A-Z characters are used in the encoding of multi-byte characters. - */ -template<class SI_CHAR> -struct SI_GenericNoCase { - inline SI_CHAR locase(SI_CHAR ch) const { - return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a'); - } - bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { - long cmp; - for ( ;*pLeft && *pRight; ++pLeft, ++pRight) { - cmp = (long) locase(*pLeft) - (long) locase(*pRight); - if (cmp != 0) { - return cmp < 0; - } - } - return *pRight != 0; - } -}; - -/** - * Null conversion class for MBCS/UTF-8 to char (or equivalent). - */ -template<class SI_CHAR> -class SI_ConvertA { - bool m_bStoreIsUtf8; -protected: - SI_ConvertA() { } -public: - SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { } - - /* copy and assignment */ - SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); } - SI_ConvertA & operator=(const SI_ConvertA & rhs) { - m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8; - return *this; - } - - /** Calculate the number of SI_CHAR required for converting the input - * from the storage format. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to SI_CHAR. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @return Number of SI_CHAR required by the string when - * converted. If there are embedded NULL bytes in the - * input data, only the string up and not including - * the NULL byte will be converted. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeFromStore( - const char * a_pInputData, - size_t a_uInputDataLen) - { - (void)a_pInputData; - SI_ASSERT(a_uInputDataLen != (size_t) -1); - - // ASCII/MBCS/UTF-8 needs no conversion - return a_uInputDataLen; - } - - /** Convert the input string from the storage format to SI_CHAR. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to SI_CHAR. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @param a_pOutputData Pointer to the output buffer to received the - * converted data. - * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. - * @return true if all of the input data was successfully - * converted. - */ - bool ConvertFromStore( - const char * a_pInputData, - size_t a_uInputDataLen, - SI_CHAR * a_pOutputData, - size_t a_uOutputDataSize) - { - // ASCII/MBCS/UTF-8 needs no conversion - if (a_uInputDataLen > a_uOutputDataSize) { - return false; - } - memcpy(a_pOutputData, a_pInputData, a_uInputDataLen); - return true; - } - - /** Calculate the number of char required by the storage format of this - * data. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated string to calculate the number of - * bytes required to be converted to storage format. - * @return Number of bytes required by the string when - * converted to storage format. This size always - * includes space for the terminating NULL character. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeToStore( - const SI_CHAR * a_pInputData) - { - // ASCII/MBCS/UTF-8 needs no conversion - return strlen((const char *)a_pInputData) + 1; - } - - /** Convert the input string to the storage format of this data. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated source string to convert. All of - * the data will be converted including the - * terminating NULL character. - * @param a_pOutputData Pointer to the buffer to receive the converted - * string. - * @param a_uOutputDataSize Size of the output buffer in char. - * @return true if all of the input data, including the - * terminating NULL character was successfully - * converted. - */ - bool ConvertToStore( - const SI_CHAR * a_pInputData, - char * a_pOutputData, - size_t a_uOutputDataSize) - { - // calc input string length (SI_CHAR type and size independent) - size_t uInputLen = strlen((const char *)a_pInputData) + 1; - if (uInputLen > a_uOutputDataSize) { - return false; - } - - // ascii/UTF-8 needs no conversion - memcpy(a_pOutputData, a_pInputData, uInputLen); - return true; - } -}; - - -// --------------------------------------------------------------------------- -// SI_CONVERT_GENERIC -// --------------------------------------------------------------------------- -#ifdef SI_CONVERT_GENERIC - -#define SI_Case SI_GenericCase -#define SI_NoCase SI_GenericNoCase - -#include <wchar.h> -#include "ConvertUTF.h" - -/** - * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference - * library functions. This can be used on all platforms. - */ -template<class SI_CHAR> -class SI_ConvertW { - bool m_bStoreIsUtf8; -protected: - SI_ConvertW() { } -public: - SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { } - - /* copy and assignment */ - SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } - SI_ConvertW & operator=(const SI_ConvertW & rhs) { - m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8; - return *this; - } - - /** Calculate the number of SI_CHAR required for converting the input - * from the storage format. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to SI_CHAR. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @return Number of SI_CHAR required by the string when - * converted. If there are embedded NULL bytes in the - * input data, only the string up and not including - * the NULL byte will be converted. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeFromStore( - const char * a_pInputData, - size_t a_uInputDataLen) - { - SI_ASSERT(a_uInputDataLen != (size_t) -1); - - if (m_bStoreIsUtf8) { - // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t - // so we just return the same number of characters required as for - // the source text. - return a_uInputDataLen; - } - else { - return mbstowcs(NULL, a_pInputData, a_uInputDataLen); - } - } - - /** Convert the input string from the storage format to SI_CHAR. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to SI_CHAR. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @param a_pOutputData Pointer to the output buffer to received the - * converted data. - * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. - * @return true if all of the input data was successfully - * converted. - */ - bool ConvertFromStore( - const char * a_pInputData, - size_t a_uInputDataLen, - SI_CHAR * a_pOutputData, - size_t a_uOutputDataSize) - { - if (m_bStoreIsUtf8) { - // This uses the Unicode reference implementation to do the - // conversion from UTF-8 to wchar_t. The required files are - // ConvertUTF.h and ConvertUTF.c which should be included in - // the distribution but are publically available from unicode.org - // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/ - ConversionResult retval; - const UTF8 * pUtf8 = (const UTF8 *) a_pInputData; - if (sizeof(wchar_t) == sizeof(UTF32)) { - UTF32 * pUtf32 = (UTF32 *) a_pOutputData; - retval = ConvertUTF8toUTF32( - &pUtf8, pUtf8 + a_uInputDataLen, - &pUtf32, pUtf32 + a_uOutputDataSize, - lenientConversion); - } - else if (sizeof(wchar_t) == sizeof(UTF16)) { - UTF16 * pUtf16 = (UTF16 *) a_pOutputData; - retval = ConvertUTF8toUTF16( - &pUtf8, pUtf8 + a_uInputDataLen, - &pUtf16, pUtf16 + a_uOutputDataSize, - lenientConversion); - } - return retval == conversionOK; - } - else { - size_t retval = mbstowcs(a_pOutputData, - a_pInputData, a_uOutputDataSize); - return retval != (size_t)(-1); - } - } - - /** Calculate the number of char required by the storage format of this - * data. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated string to calculate the number of - * bytes required to be converted to storage format. - * @return Number of bytes required by the string when - * converted to storage format. This size always - * includes space for the terminating NULL character. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeToStore( - const SI_CHAR * a_pInputData) - { - if (m_bStoreIsUtf8) { - // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char - size_t uLen = 0; - while (a_pInputData[uLen]) { - ++uLen; - } - return (6 * uLen) + 1; - } - else { - size_t uLen = wcstombs(NULL, a_pInputData, 0); - if (uLen == (size_t)(-1)) { - return uLen; - } - return uLen + 1; // include NULL terminator - } - } - - /** Convert the input string to the storage format of this data. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated source string to convert. All of - * the data will be converted including the - * terminating NULL character. - * @param a_pOutputData Pointer to the buffer to receive the converted - * string. - * @param a_uOutputDataSize Size of the output buffer in char. - * @return true if all of the input data, including the - * terminating NULL character was successfully - * converted. - */ - bool ConvertToStore( - const SI_CHAR * a_pInputData, - char * a_pOutputData, - size_t a_uOutputDataSize - ) - { - if (m_bStoreIsUtf8) { - // calc input string length (SI_CHAR type and size independent) - size_t uInputLen = 0; - while (a_pInputData[uInputLen]) { - ++uInputLen; - } - ++uInputLen; // include the NULL char - - // This uses the Unicode reference implementation to do the - // conversion from wchar_t to UTF-8. The required files are - // ConvertUTF.h and ConvertUTF.c which should be included in - // the distribution but are publically available from unicode.org - // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/ - ConversionResult retval; - UTF8 * pUtf8 = (UTF8 *) a_pOutputData; - if (sizeof(wchar_t) == sizeof(UTF32)) { - const UTF32 * pUtf32 = (const UTF32 *) a_pInputData; - retval = ConvertUTF32toUTF8( - &pUtf32, pUtf32 + uInputLen, - &pUtf8, pUtf8 + a_uOutputDataSize, - lenientConversion); - } - else if (sizeof(wchar_t) == sizeof(UTF16)) { - const UTF16 * pUtf16 = (const UTF16 *) a_pInputData; - retval = ConvertUTF16toUTF8( - &pUtf16, pUtf16 + uInputLen, - &pUtf8, pUtf8 + a_uOutputDataSize, - lenientConversion); - } - return retval == conversionOK; - } - else { - size_t retval = wcstombs(a_pOutputData, - a_pInputData, a_uOutputDataSize); - return retval != (size_t) -1; - } - } -}; - -#endif // SI_CONVERT_GENERIC - - -// --------------------------------------------------------------------------- -// SI_CONVERT_ICU -// --------------------------------------------------------------------------- -#ifdef SI_CONVERT_ICU - -#define SI_Case SI_GenericCase -#define SI_NoCase SI_GenericNoCase - -#include <unicode/ucnv.h> - -/** - * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms. - */ -template<class SI_CHAR> -class SI_ConvertW { - const char * m_pEncoding; - UConverter * m_pConverter; -protected: - SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { } -public: - SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) { - m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL; - } - - /* copy and assignment */ - SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } - SI_ConvertW & operator=(const SI_ConvertW & rhs) { - m_pEncoding = rhs.m_pEncoding; - m_pConverter = NULL; - return *this; - } - ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); } - - /** Calculate the number of UChar required for converting the input - * from the storage format. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to UChar. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @return Number of UChar required by the string when - * converted. If there are embedded NULL bytes in the - * input data, only the string up and not including - * the NULL byte will be converted. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeFromStore( - const char * a_pInputData, - size_t a_uInputDataLen) - { - SI_ASSERT(a_uInputDataLen != (size_t) -1); - - UErrorCode nError; - - if (!m_pConverter) { - nError = U_ZERO_ERROR; - m_pConverter = ucnv_open(m_pEncoding, &nError); - if (U_FAILURE(nError)) { - return (size_t) -1; - } - } - - nError = U_ZERO_ERROR; - ucnv_resetToUnicode(m_pConverter); - int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0, - a_pInputData, (int32_t) a_uInputDataLen, &nError); - if (nError != U_BUFFER_OVERFLOW_ERROR) { - return (size_t) -1; - } - - return (size_t) nLen; - } - - /** Convert the input string from the storage format to UChar. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to UChar. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @param a_pOutputData Pointer to the output buffer to received the - * converted data. - * @param a_uOutputDataSize Size of the output buffer in UChar. - * @return true if all of the input data was successfully - * converted. - */ - bool ConvertFromStore( - const char * a_pInputData, - size_t a_uInputDataLen, - UChar * a_pOutputData, - size_t a_uOutputDataSize) - { - UErrorCode nError; - - if (!m_pConverter) { - nError = U_ZERO_ERROR; - m_pConverter = ucnv_open(m_pEncoding, &nError); - if (U_FAILURE(nError)) { - return false; - } - } - - nError = U_ZERO_ERROR; - ucnv_resetToUnicode(m_pConverter); - ucnv_toUChars(m_pConverter, - a_pOutputData, (int32_t) a_uOutputDataSize, - a_pInputData, (int32_t) a_uInputDataLen, &nError); - if (U_FAILURE(nError)) { - return false; - } - - return true; - } - - /** Calculate the number of char required by the storage format of this - * data. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated string to calculate the number of - * bytes required to be converted to storage format. - * @return Number of bytes required by the string when - * converted to storage format. This size always - * includes space for the terminating NULL character. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeToStore( - const UChar * a_pInputData) - { - UErrorCode nError; - - if (!m_pConverter) { - nError = U_ZERO_ERROR; - m_pConverter = ucnv_open(m_pEncoding, &nError); - if (U_FAILURE(nError)) { - return (size_t) -1; - } - } - - nError = U_ZERO_ERROR; - ucnv_resetFromUnicode(m_pConverter); - int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0, - a_pInputData, -1, &nError); - if (nError != U_BUFFER_OVERFLOW_ERROR) { - return (size_t) -1; - } - - return (size_t) nLen + 1; - } - - /** Convert the input string to the storage format of this data. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated source string to convert. All of - * the data will be converted including the - * terminating NULL character. - * @param a_pOutputData Pointer to the buffer to receive the converted - * string. - * @param a_pOutputDataSize Size of the output buffer in char. - * @return true if all of the input data, including the - * terminating NULL character was successfully - * converted. - */ - bool ConvertToStore( - const UChar * a_pInputData, - char * a_pOutputData, - size_t a_uOutputDataSize) - { - UErrorCode nError; - - if (!m_pConverter) { - nError = U_ZERO_ERROR; - m_pConverter = ucnv_open(m_pEncoding, &nError); - if (U_FAILURE(nError)) { - return false; - } - } - - nError = U_ZERO_ERROR; - ucnv_resetFromUnicode(m_pConverter); - ucnv_fromUChars(m_pConverter, - a_pOutputData, (int32_t) a_uOutputDataSize, - a_pInputData, -1, &nError); - if (U_FAILURE(nError)) { - return false; - } - - return true; - } -}; - -#endif // SI_CONVERT_ICU - - -// --------------------------------------------------------------------------- -// SI_CONVERT_WIN32 -// --------------------------------------------------------------------------- -#ifdef SI_CONVERT_WIN32 - -#define SI_Case SI_GenericCase - -// Windows CE doesn't have errno or MBCS libraries -#ifdef _WIN32_WCE -# ifndef SI_NO_MBCS -# define SI_NO_MBCS -# endif -#endif - -#include <windows.h> -#ifdef SI_NO_MBCS -# define SI_NoCase SI_GenericNoCase -#else // !SI_NO_MBCS -/** - * Case-insensitive comparison class using Win32 MBCS functions. This class - * returns a case-insensitive semi-collation order for MBCS text. It may not - * be safe for UTF-8 text returned in char format as we don't know what - * characters will be folded by the function! Therefore, if you are using - * SI_CHAR == char and SetUnicode(true), then you need to use the generic - * SI_NoCase class instead. - */ -#include <mbstring.h> -template<class SI_CHAR> -struct SI_NoCase { - bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { - if (sizeof(SI_CHAR) == sizeof(char)) { - return _mbsicmp((const unsigned char *)pLeft, - (const unsigned char *)pRight) < 0; - } - if (sizeof(SI_CHAR) == sizeof(wchar_t)) { - return _wcsicmp((const wchar_t *)pLeft, - (const wchar_t *)pRight) < 0; - } - return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight); - } -}; -#endif // SI_NO_MBCS - -/** - * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses - * only the Win32 functions and doesn't require the external Unicode UTF-8 - * conversion library. It will not work on Windows 95 without using Microsoft - * Layer for Unicode in your application. - */ -template<class SI_CHAR> -class SI_ConvertW { - UINT m_uCodePage; -protected: - SI_ConvertW() { } -public: - SI_ConvertW(bool a_bStoreIsUtf8) { - m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP; - } - - /* copy and assignment */ - SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } - SI_ConvertW & operator=(const SI_ConvertW & rhs) { - m_uCodePage = rhs.m_uCodePage; - return *this; - } - - /** Calculate the number of SI_CHAR required for converting the input - * from the storage format. The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to SI_CHAR. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @return Number of SI_CHAR required by the string when - * converted. If there are embedded NULL bytes in the - * input data, only the string up and not including - * the NULL byte will be converted. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeFromStore( - const char * a_pInputData, - size_t a_uInputDataLen) - { - SI_ASSERT(a_uInputDataLen != (size_t) -1); - - int retval = MultiByteToWideChar( - m_uCodePage, 0, - a_pInputData, (int) a_uInputDataLen, - 0, 0); - return (size_t)(retval > 0 ? retval : -1); - } - - /** Convert the input string from the storage format to SI_CHAR. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData Data in storage format to be converted to SI_CHAR. - * @param a_uInputDataLen Length of storage format data in bytes. This - * must be the actual length of the data, including - * NULL byte if NULL terminated string is required. - * @param a_pOutputData Pointer to the output buffer to received the - * converted data. - * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. - * @return true if all of the input data was successfully - * converted. - */ - bool ConvertFromStore( - const char * a_pInputData, - size_t a_uInputDataLen, - SI_CHAR * a_pOutputData, - size_t a_uOutputDataSize) - { - int nSize = MultiByteToWideChar( - m_uCodePage, 0, - a_pInputData, (int) a_uInputDataLen, - (wchar_t *) a_pOutputData, (int) a_uOutputDataSize); - return (nSize > 0); - } - - /** Calculate the number of char required by the storage format of this - * data. The storage format is always UTF-8. - * - * @param a_pInputData NULL terminated string to calculate the number of - * bytes required to be converted to storage format. - * @return Number of bytes required by the string when - * converted to storage format. This size always - * includes space for the terminating NULL character. - * @return -1 cast to size_t on a conversion error. - */ - size_t SizeToStore( - const SI_CHAR * a_pInputData) - { - int retval = WideCharToMultiByte( - m_uCodePage, 0, - (const wchar_t *) a_pInputData, -1, - 0, 0, 0, 0); - return (size_t) (retval > 0 ? retval : -1); - } - - /** Convert the input string to the storage format of this data. - * The storage format is always UTF-8 or MBCS. - * - * @param a_pInputData NULL terminated source string to convert. All of - * the data will be converted including the - * terminating NULL character. - * @param a_pOutputData Pointer to the buffer to receive the converted - * string. - * @param a_pOutputDataSize Size of the output buffer in char. - * @return true if all of the input data, including the - * terminating NULL character was successfully - * converted. - */ - bool ConvertToStore( - const SI_CHAR * a_pInputData, - char * a_pOutputData, - size_t a_uOutputDataSize) - { - int retval = WideCharToMultiByte( - m_uCodePage, 0, - (const wchar_t *) a_pInputData, -1, - a_pOutputData, (int) a_uOutputDataSize, 0, 0); - return retval > 0; - } -}; - -#endif // SI_CONVERT_WIN32 - - -// --------------------------------------------------------------------------- -// TYPE DEFINITIONS -// --------------------------------------------------------------------------- - -typedef CSimpleIniTempl<char, - SI_NoCase<char>,SI_ConvertA<char> > CSimpleIniA; -typedef CSimpleIniTempl<char, - SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA; - -#if defined(SI_CONVERT_ICU) -typedef CSimpleIniTempl<UChar, - SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW; -typedef CSimpleIniTempl<UChar, - SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW; -#else -typedef CSimpleIniTempl<wchar_t, - SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW; -typedef CSimpleIniTempl<wchar_t, - SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW; -#endif - -#ifdef _UNICODE -# define CSimpleIni CSimpleIniW -# define CSimpleIniCase CSimpleIniCaseW -# define SI_NEWLINE SI_NEWLINE_W -#else // !_UNICODE -# define CSimpleIni CSimpleIniA -# define CSimpleIniCase CSimpleIniCaseA -# define SI_NEWLINE SI_NEWLINE_A -#endif // _UNICODE - -#ifdef _MSC_VER -# pragma warning (pop) -#endif - -#endif // INCLUDED_SimpleIni_h - +#include <string>
+#include <map>
+#include <list>
+#include <algorithm>
+#include <stdio.h>
+
+#ifdef SI_SUPPORT_IOSTREAMS
+# include <iostream>
+#endif // SI_SUPPORT_IOSTREAMS
+
+#ifdef _DEBUG
+# ifndef assert
+# include <cassert>
+# endif
+# define SI_ASSERT(x) assert(x)
+#else
+# define SI_ASSERT(x)
+#endif
+
+enum SI_Error {
+ SI_OK = 0, //!< No error
+ SI_UPDATED = 1, //!< An existing value was updated
+ SI_INSERTED = 2, //!< A new value was inserted
+
+ // note: test for any error with (retval < 0)
+ SI_FAIL = -1, //!< Generic failure
+ SI_NOMEM = -2, //!< Out of memory error
+ SI_FILE = -3 //!< File error (see errno for detail error)
+};
+
+#define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
+
+#ifdef _WIN32
+# define SI_NEWLINE_A "\r\n"
+# define SI_NEWLINE_W L"\r\n"
+#else // !_WIN32
+# define SI_NEWLINE_A "\n"
+# define SI_NEWLINE_W L"\n"
+#endif // _WIN32
+
+#if defined(SI_CONVERT_ICU)
+# include <unicode/ustring.h>
+#endif
+
+#if defined(_WIN32)
+# define SI_HAS_WIDE_FILE
+# define SI_WCHAR_T wchar_t
+#elif defined(SI_CONVERT_ICU)
+# define SI_HAS_WIDE_FILE
+# define SI_WCHAR_T UChar
+#endif
+
+
+// ---------------------------------------------------------------------------
+// MAIN TEMPLATE CLASS
+// ---------------------------------------------------------------------------
+
+/** Simple INI file reader.
+
+ This can be instantiated with the choice of unicode or native characterset,
+ and case sensitive or insensitive comparisons of section and key names.
+ The supported combinations are pre-defined with the following typedefs:
+
+ <table>
+ <tr><th>Interface <th>Case-sensitive <th>Typedef
+ <tr><td>char <td>No <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>CSimpleIniCaseA
+ <tr><td>wchar_t <td>No <td>CSimpleIniW
+ <tr><td>wchar_t <td>Yes <td>CSimpleIniCaseW
+ </table>
+
+ Note that using other types for the SI_CHAR is supported. For instance,
+ unsigned char, unsigned short, etc. Note that where the alternative type
+ is a different size to char/wchar_t you may need to supply new helper
+ classes for SI_STRLESS and SI_CONVERTER.
+ */
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+class CSimpleIniTempl
+{
+public:
+ /** key entry */
+ struct Entry {
+ const SI_CHAR * pItem;
+ const SI_CHAR * pComment;
+ int nOrder;
+
+ Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0)
+ : pItem(a_pszItem)
+ , pComment(NULL)
+ , nOrder(a_nOrder)
+ { }
+ Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder)
+ : pItem(a_pszItem)
+ , pComment(a_pszComment)
+ , nOrder(a_nOrder)
+ { }
+ Entry(const Entry & rhs) { operator=(rhs); }
+ Entry & operator=(const Entry & rhs) {
+ pItem = rhs.pItem;
+ pComment = rhs.pComment;
+ nOrder = rhs.nOrder;
+ return *this;
+ }
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+ /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */
+ bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); }
+ bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); }
+#endif
+
+ /** Strict less ordering by name of key only */
+ struct KeyOrder : std::binary_function<Entry, Entry, bool> {
+ bool operator()(const Entry & lhs, const Entry & rhs) const {
+ const static SI_STRLESS isLess = SI_STRLESS();
+ return isLess(lhs.pItem, rhs.pItem);
+ }
+ };
+
+ /** Strict less ordering by order, and then name of key */
+ struct LoadOrder : std::binary_function<Entry, Entry, bool> {
+ bool operator()(const Entry & lhs, const Entry & rhs) const {
+ if (lhs.nOrder != rhs.nOrder) {
+ return lhs.nOrder < rhs.nOrder;
+ }
+ return KeyOrder()(lhs.pItem, rhs.pItem);
+ }
+ };
+ };
+
+ /** map keys to values */
+ typedef std::multimap<Entry,const SI_CHAR *,typename Entry::KeyOrder> TKeyVal;
+
+ /** map sections to key/value map */
+ typedef std::map<Entry,TKeyVal,typename Entry::KeyOrder> TSection;
+
+ /** set of dependent string pointers. Note that these pointers are
+ dependent on memory owned by CSimpleIni.
+ */
+ typedef std::list<Entry> TNamesDepend;
+
+ /** interface definition for the OutputWriter object to pass to Save()
+ in order to output the INI file data.
+ */
+ class OutputWriter {
+ public:
+ OutputWriter() { }
+ virtual ~OutputWriter() { }
+ virtual void Write(const char * a_pBuf) = 0;
+ private:
+ OutputWriter(const OutputWriter &); // disable
+ OutputWriter & operator=(const OutputWriter &); // disable
+ };
+
+ /** OutputWriter class to write the INI data to a file */
+ class FileWriter : public OutputWriter {
+ FILE * m_file;
+ public:
+ FileWriter(FILE * a_file) : m_file(a_file) { }
+ void Write(const char * a_pBuf) {
+ fputs(a_pBuf, m_file);
+ }
+ private:
+ FileWriter(const FileWriter &); // disable
+ FileWriter & operator=(const FileWriter &); // disable
+ };
+
+ /** OutputWriter class to write the INI data to a string */
+ class StringWriter : public OutputWriter {
+ std::string & m_string;
+ public:
+ StringWriter(std::string & a_string) : m_string(a_string) { }
+ void Write(const char * a_pBuf) {
+ m_string.append(a_pBuf);
+ }
+ private:
+ StringWriter(const StringWriter &); // disable
+ StringWriter & operator=(const StringWriter &); // disable
+ };
+
+#ifdef SI_SUPPORT_IOSTREAMS
+ /** OutputWriter class to write the INI data to an ostream */
+ class StreamWriter : public OutputWriter {
+ std::ostream & m_ostream;
+ public:
+ StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { }
+ void Write(const char * a_pBuf) {
+ m_ostream << a_pBuf;
+ }
+ private:
+ StreamWriter(const StreamWriter &); // disable
+ StreamWriter & operator=(const StreamWriter &); // disable
+ };
+#endif // SI_SUPPORT_IOSTREAMS
+
+ /** Characterset conversion utility class to convert strings to the
+ same format as is used for the storage.
+ */
+ class Converter : private SI_CONVERTER {
+ public:
+ Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) {
+ m_scratch.resize(1024);
+ }
+ Converter(const Converter & rhs) { operator=(rhs); }
+ Converter & operator=(const Converter & rhs) {
+ m_scratch = rhs.m_scratch;
+ return *this;
+ }
+ bool ConvertToStore(const SI_CHAR * a_pszString) {
+ size_t uLen = SizeToStore(a_pszString);
+ if (uLen == (size_t)(-1)) {
+ return false;
+ }
+ while (uLen > m_scratch.size()) {
+ m_scratch.resize(m_scratch.size() * 2);
+ }
+ return SI_CONVERTER::ConvertToStore(
+ a_pszString,
+ const_cast<char*>(m_scratch.data()),
+ m_scratch.size());
+ }
+ const char * Data() { return m_scratch.data(); }
+ private:
+ std::string m_scratch;
+ };
+
+public:
+ /*-----------------------------------------------------------------------*/
+
+ /** Default constructor.
+
+ @param a_bIsUtf8 See the method SetUnicode() for details.
+ @param a_bMultiKey See the method SetMultiKey() for details.
+ @param a_bMultiLine See the method SetMultiLine() for details.
+ */
+ CSimpleIniTempl(
+ bool a_bIsUtf8 = false,
+ bool a_bMultiKey = false,
+ bool a_bMultiLine = false
+ );
+
+ /** Destructor */
+ ~CSimpleIniTempl();
+
+ /** Deallocate all memory stored by this object */
+ void Reset();
+
+ /*-----------------------------------------------------------------------*/
+ /** @{ @name Settings */
+
+ /** Set the storage format of the INI data. This affects both the loading
+ and saving of the INI data using all of the Load/Save API functions.
+ This value cannot be changed after any INI data has been loaded.
+
+ If the file is not set to Unicode (UTF-8), then the data encoding is
+ assumed to be the OS native encoding. This encoding is the system
+ locale on Linux/Unix and the legacy MBCS encoding on Windows NT/2K/XP.
+ If the storage format is set to Unicode then the file will be loaded
+ as UTF-8 encoded data regardless of the native file encoding. If
+ SI_CHAR == char then all of the char* parameters take and return UTF-8
+ encoded data regardless of the system locale.
+
+ \param a_bIsUtf8 Assume UTF-8 encoding for the source?
+ */
+ void SetUnicode(bool a_bIsUtf8 = true) {
+ if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8;
+ }
+
+ /** Get the storage format of the INI data. */
+ bool IsUnicode() const { return m_bStoreIsUtf8; }
+
+ /** Should multiple identical keys be permitted in the file. If set to false
+ then the last value encountered will be used as the value of the key.
+ If set to true, then all values will be available to be queried. For
+ example, with the following input:
+
+ <pre>
+ [section]
+ test=value1
+ test=value2
+ </pre>
+
+ Then with SetMultiKey(true), both of the values "value1" and "value2"
+ will be returned for the key test. If SetMultiKey(false) is used, then
+ the value for "test" will only be "value2". This value may be changed
+ at any time.
+
+ \param a_bAllowMultiKey Allow multi-keys in the source?
+ */
+ void SetMultiKey(bool a_bAllowMultiKey = true) {
+ m_bAllowMultiKey = a_bAllowMultiKey;
+ }
+
+ /** Get the storage format of the INI data. */
+ bool IsMultiKey() const { return m_bAllowMultiKey; }
+
+ /** Should data values be permitted to span multiple lines in the file. If
+ set to false then the multi-line construct <<<TAG as a value will be
+ returned as is instead of loading the data. This value may be changed
+ at any time.
+
+ \param a_bAllowMultiLine Allow multi-line values in the source?
+ */
+ void SetMultiLine(bool a_bAllowMultiLine = true) {
+ m_bAllowMultiLine = a_bAllowMultiLine;
+ }
+
+ /** Query the status of multi-line data */
+ bool IsMultiLine() const { return m_bAllowMultiLine; }
+
+ /*-----------------------------------------------------------------------*/
+ /** @}
+ @{ @name Loading INI Data */
+
+ /** Load an INI file from disk into memory
+
+ @param a_pszFile Path of the file to be loaded. This will be passed
+ to fopen() and so must be a valid path for the
+ current platform.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadFile(
+ const char * a_pszFile
+ );
+
+#ifdef SI_HAS_WIDE_FILE
+ /** Load an INI file from disk into memory
+
+ @param a_pwszFile Path of the file to be loaded in UTF-16.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadFile(
+ const SI_WCHAR_T * a_pwszFile
+ );
+#endif // SI_HAS_WIDE_FILE
+
+ /** Load the file from a file pointer.
+
+ @param a_fpFile Valid file pointer to read the file data from. The
+ file will be read until end of file.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadFile(
+ FILE * a_fpFile
+ );
+
+#ifdef SI_SUPPORT_IOSTREAMS
+ /** Load INI file data from an istream.
+
+ @param a_istream Stream to read from
+
+ @return SI_Error See error definitions
+ */
+ SI_Error Load(
+ std::istream & a_istream
+ );
+#endif // SI_SUPPORT_IOSTREAMS
+
+ /** Load INI file data direct from a std::string
+
+ @param a_strData Data to be loaded
+
+ @return SI_Error See error definitions
+ */
+ SI_Error Load(const std::string & a_strData) {
+ return Load(a_strData.c_str(), a_strData.size());
+ }
+
+ /** Load INI file data direct from memory
+
+ @param a_pData Data to be loaded
+ @param a_uDataLen Length of the data in bytes
+
+ @return SI_Error See error definitions
+ */
+ SI_Error Load(
+ const char * a_pData,
+ size_t a_uDataLen
+ );
+
+ /*-----------------------------------------------------------------------*/
+ /** @}
+ @{ @name Saving INI Data */
+
+ /** Save an INI file from memory to disk
+
+ @param a_pszFile Path of the file to be saved. This will be passed
+ to fopen() and so must be a valid path for the
+ current platform.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is
+ in UTF-8 format. If it is not UTF-8 then
+ this parameter is ignored.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error SaveFile(
+ const char * a_pszFile,
+ bool a_bAddSignature = true
+ ) const;
+
+#ifdef SI_HAS_WIDE_FILE
+ /** Save an INI file from memory to disk
+
+ @param a_pwszFile Path of the file to be saved in UTF-16.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is
+ in UTF-8 format. If it is not UTF-8 then
+ this parameter is ignored.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error SaveFile(
+ const SI_WCHAR_T * a_pwszFile,
+ bool a_bAddSignature = true
+ ) const;
+#endif // _WIN32
+
+ /** Save the INI data to a file. See Save() for details.
+
+ @param a_pFile Handle to a file. File should be opened for
+ binary output.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
+ UTF-8 format. If it is not UTF-8 then this value is
+ ignored. Do not set this to true if anything has
+ already been written to the file.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error SaveFile(
+ FILE * a_pFile,
+ bool a_bAddSignature = false
+ ) const;
+
+ /** Save the INI data. The data will be written to the output device
+ in a format appropriate to the current data, selected by:
+
+ <table>
+ <tr><th>SI_CHAR <th>FORMAT
+ <tr><td>char <td>same format as when loaded (MBCS or UTF-8)
+ <tr><td>wchar_t <td>UTF-8
+ <tr><td>other <td>UTF-8
+ </table>
+
+ Note that comments from the original data is preserved as per the
+ documentation on comments. The order of the sections and values
+ from the original file will be preserved.
+
+ Any data prepended or appended to the output device must use the the
+ same format (MBCS or UTF-8). You may use the GetConverter() method to
+ convert text to the correct format regardless of the output format
+ being used by SimpleIni.
+
+ To add a BOM to UTF-8 data, write it out manually at the very beginning
+ like is done in SaveFile when a_bUseBOM is true.
+
+ @param a_oOutput Output writer to write the data to.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
+ UTF-8 format. If it is not UTF-8 then this value is
+ ignored. Do not set this to true if anything has
+ already been written to the OutputWriter.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error Save(
+ OutputWriter & a_oOutput,
+ bool a_bAddSignature = false
+ ) const;
+
+#ifdef SI_SUPPORT_IOSTREAMS
+ /** Save the INI data to an ostream. See Save() for details.
+
+ @param a_ostream String to have the INI data appended to.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
+ UTF-8 format. If it is not UTF-8 then this value is
+ ignored. Do not set this to true if anything has
+ already been written to the stream.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error Save(
+ std::ostream & a_ostream,
+ bool a_bAddSignature = false
+ ) const
+ {
+ StreamWriter writer(a_ostream);
+ return Save(writer, a_bAddSignature);
+ }
+#endif // SI_SUPPORT_IOSTREAMS
+
+ /** Append the INI data to a string. See Save() for details.
+
+ @param a_sBuffer String to have the INI data appended to.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
+ UTF-8 format. If it is not UTF-8 then this value is
+ ignored. Do not set this to true if anything has
+ already been written to the string.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error Save(
+ std::string & a_sBuffer,
+ bool a_bAddSignature = false
+ ) const
+ {
+ StringWriter writer(a_sBuffer);
+ return Save(writer, a_bAddSignature);
+ }
+
+ /*-----------------------------------------------------------------------*/
+ /** @}
+ @{ @name Accessing INI Data */
+
+ /** Retrieve all section names. The list is returned as an STL vector of
+ names and can be iterated or searched as necessary. Note that the
+ sort order of the returned strings is NOT DEFINED. You can sort
+ the names into the load order if desired. Search this file for ".sort"
+ for an example.
+
+ NOTE! This structure contains only pointers to strings. The actual
+ string data is stored in memory owned by CSimpleIni. Ensure that the
+ CSimpleIni object is not destroyed or Reset() while these pointers
+ are in use!
+
+ @param a_names Vector that will receive all of the section
+ names. See note above!
+ */
+ void GetAllSections(
+ TNamesDepend & a_names
+ ) const;
+
+ /** Retrieve all unique key names in a section. The sort order of the
+ returned strings is NOT DEFINED. You can sort the names into the load
+ order if desired. Search this file for ".sort" for an example. Only
+ unique key names are returned.
+
+ NOTE! This structure contains only pointers to strings. The actual
+ string data is stored in memory owned by CSimpleIni. Ensure that the
+ CSimpleIni object is not destroyed or Reset() while these strings
+ are in use!
+
+ @param a_pSection Section to request data for
+ @param a_names List that will receive all of the key
+ names. See note above!
+
+ @return true Section was found.
+ @return false Matching section was not found.
+ */
+ bool GetAllKeys(
+ const SI_CHAR * a_pSection,
+ TNamesDepend & a_names
+ ) const;
+
+ /** Retrieve all values for a specific key. This method can be used when
+ multiple keys are both enabled and disabled. Note that the sort order
+ of the returned strings is NOT DEFINED. You can sort the names into
+ the load order if desired. Search this file for ".sort" for an example.
+
+ NOTE! The returned values are pointers to string data stored in memory
+ owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
+ or Reset while you are using this pointer!
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_values List to return if the key is not found
+
+ @return true Key was found.
+ @return false Matching section/key was not found.
+ */
+ bool GetAllValues(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ TNamesDepend & a_values
+ ) const;
+
+ /** Query the number of keys in a specific section. Note that if multiple
+ keys are enabled, then this value may be different to the number of
+ keys returned by GetAllKeys.
+
+ @param a_pSection Section to request data for
+
+ @return -1 Section does not exist in the file
+ @return >=0 Number of keys in the section
+ */
+ int GetSectionSize(
+ const SI_CHAR * a_pSection
+ ) const;
+
+ /** Retrieve all key and value pairs for a section. The data is returned
+ as a pointer to an STL map and can be iterated or searched as
+ desired. Note that multiple entries for the same key may exist when
+ multiple keys have been enabled.
+
+ NOTE! This structure contains only pointers to strings. The actual
+ string data is stored in memory owned by CSimpleIni. Ensure that the
+ CSimpleIni object is not destroyed or Reset() while these strings
+ are in use!
+
+ @param a_pSection Name of the section to return
+ @return boolean Was a section matching the supplied
+ name found.
+ */
+ const TKeyVal * GetSection(
+ const SI_CHAR * a_pSection
+ ) const;
+
+ /** Retrieve the value for a specific key. If multiple keys are enabled
+ (see SetMultiKey) then only the first value associated with that key
+ will be returned, see GetAllValues for getting all values with multikey.
+
+ NOTE! The returned value is a pointer to string data stored in memory
+ owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
+ or Reset while you are using this pointer!
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_pDefault Value to return if the key is not found
+ @param a_pHasMultiple Optionally receive notification of if there are
+ multiple entries for this key.
+
+ @return a_pDefault Key was not found in the section
+ @return other Value of the key
+ */
+ const SI_CHAR * GetValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pDefault = NULL,
+ bool * a_pHasMultiple = NULL
+ ) const;
+
+ /** Retrieve a numeric value for a specific key. If multiple keys are enabled
+ (see SetMultiKey) then only the first value associated with that key
+ will be returned, see GetAllValues for getting all values with multikey.
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_nDefault Value to return if the key is not found
+ @param a_pHasMultiple Optionally receive notification of if there are
+ multiple entries for this key.
+
+ @return a_nDefault Key was not found in the section
+ @return other Value of the key
+ */
+ long GetLongValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ long a_nDefault = 0,
+ bool * a_pHasMultiple = NULL
+ ) const;
+
+ /** Retrieve a boolean value for a specific key. If multiple keys are enabled
+ (see SetMultiKey) then only the first value associated with that key
+ will be returned, see GetAllValues for getting all values with multikey.
+
+ Strings starting with "t", "y", "on" or "1" are returned as logically true.
+ Strings starting with "f", "n", "of" or "0" are returned as logically false.
+ For all other values the default is returned. Character comparisons are
+ case-insensitive.
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_bDefault Value to return if the key is not found
+ @param a_pHasMultiple Optionally receive notification of if there are
+ multiple entries for this key.
+
+ @return a_nDefault Key was not found in the section
+ @return other Value of the key
+ */
+ bool GetBoolValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bDefault = false,
+ bool * a_pHasMultiple = NULL
+ ) const;
+
+ /** Add or update a section or value. This will always insert
+ when multiple keys are enabled.
+
+ @param a_pSection Section to add or update
+ @param a_pKey Key to add or update. Set to NULL to
+ create an empty section.
+ @param a_pValue Value to set. Set to NULL to create an
+ empty section.
+ @param a_pComment Comment to be associated with the section or the
+ key. If a_pKey is NULL then it will be associated
+ with the section, otherwise the key. Note that a
+ comment may be set ONLY when the section or key is
+ first created (i.e. when this function returns the
+ value SI_INSERTED). If you wish to create a section
+ with a comment then you need to create the section
+ separately to the key. The comment string must be
+ in full comment form already (have a comment
+ character starting every line).
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/SetValue and SetValue
+ with a_bForceReplace = true, is that the load
+ order and comment will be preserved this way.
+
+ @return SI_Error See error definitions
+ @return SI_UPDATED Value was updated
+ @return SI_INSERTED Value was inserted
+ */
+ SI_Error SetValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pValue,
+ const SI_CHAR * a_pComment = NULL,
+ bool a_bForceReplace = false
+ )
+ {
+ return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true);
+ }
+
+ /** Add or update a numeric value. This will always insert
+ when multiple keys are enabled.
+
+ @param a_pSection Section to add or update
+ @param a_pKey Key to add or update.
+ @param a_nValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
+ notes on SetValue() for comments.
+ @param a_bUseHex By default the value will be written to the file
+ in decimal format. Set this to true to write it
+ as hexadecimal.
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/SetLongValue and
+ SetLongValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
+ way.
+
+ @return SI_Error See error definitions
+ @return SI_UPDATED Value was updated
+ @return SI_INSERTED Value was inserted
+ */
+ SI_Error SetLongValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ long a_nValue,
+ const SI_CHAR * a_pComment = NULL,
+ bool a_bUseHex = false,
+ bool a_bForceReplace = false
+ );
+
+ /** Add or update a boolean value. This will always insert
+ when multiple keys are enabled.
+
+ @param a_pSection Section to add or update
+ @param a_pKey Key to add or update.
+ @param a_bValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
+ notes on SetValue() for comments.
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/SetBoolValue and
+ SetBoolValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
+ way.
+
+ @return SI_Error See error definitions
+ @return SI_UPDATED Value was updated
+ @return SI_INSERTED Value was inserted
+ */
+ SI_Error SetBoolValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_nValue,
+ const SI_CHAR * a_pComment = NULL,
+ bool a_bForceReplace = false
+ );
+
+ /** Delete an entire section, or a key from a section. Note that the
+ data returned by GetSection is invalid and must not be used after
+ anything has been deleted from that section using this method.
+ Note when multiple keys is enabled, this will delete all keys with
+ that name; there is no way to selectively delete individual key/values
+ in this situation.
+
+ @param a_pSection Section to delete key from, or if
+ a_pKey is NULL, the section to remove.
+ @param a_pKey Key to remove from the section. Set to
+ NULL to remove the entire section.
+ @param a_bRemoveEmpty If the section is empty after this key has
+ been deleted, should the empty section be
+ removed?
+
+ @return true Key or section was deleted.
+ @return false Key or section was not found.
+ */
+ bool Delete(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bRemoveEmpty = false
+ );
+
+ /*-----------------------------------------------------------------------*/
+ /** @}
+ @{ @name Converter */
+
+ /** Return a conversion object to convert text to the same encoding
+ as is used by the Save(), SaveFile() and SaveString() functions.
+ Use this to prepare the strings that you wish to append or prepend
+ to the output INI data.
+ */
+ Converter GetConverter() const {
+ return Converter(m_bStoreIsUtf8);
+ }
+
+ /*-----------------------------------------------------------------------*/
+ /** @} */
+
+private:
+ // copying is not permitted
+ CSimpleIniTempl(const CSimpleIniTempl &); // disabled
+ CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled
+
+ /** Parse the data looking for a file comment and store it if found.
+ */
+ SI_Error FindFileComment(
+ SI_CHAR *& a_pData,
+ bool a_bCopyStrings
+ );
+
+ /** Parse the data looking for the next valid entry. The memory pointed to
+ by a_pData is modified by inserting NULL characters. The pointer is
+ updated to the current location in the block of text.
+ */
+ bool FindEntry(
+ SI_CHAR *& a_pData,
+ const SI_CHAR *& a_pSection,
+ const SI_CHAR *& a_pKey,
+ const SI_CHAR *& a_pVal,
+ const SI_CHAR *& a_pComment
+ ) const;
+
+ /** Add the section/key/value to our data.
+
+ @param a_pSection Section name. Sections will be created if they
+ don't already exist.
+ @param a_pKey Key name. May be NULL to create an empty section.
+ Existing entries will be updated. New entries will
+ be created.
+ @param a_pValue Value for the key.
+ @param a_pComment Comment to be associated with the section or the
+ key. If a_pKey is NULL then it will be associated
+ with the section, otherwise the key. This must be
+ a string in full comment form already (have a
+ comment character starting every line).
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/AddEntry and AddEntry
+ with a_bForceReplace = true, is that the load
+ order and comment will be preserved this way.
+ @param a_bCopyStrings Should copies of the strings be made or not.
+ If false then the pointers will be used as is.
+ */
+ SI_Error AddEntry(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pValue,
+ const SI_CHAR * a_pComment,
+ bool a_bForceReplace,
+ bool a_bCopyStrings
+ );
+
+ /** Is the supplied character a whitespace character? */
+ inline bool IsSpace(SI_CHAR ch) const {
+ return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
+ }
+
+ /** Does the supplied character start a comment line? */
+ inline bool IsComment(SI_CHAR ch) const {
+ return (ch == ';' || ch == '#');
+ }
+
+
+ /** Skip over a newline character (or characters) for either DOS or UNIX */
+ inline void SkipNewLine(SI_CHAR *& a_pData) const {
+ a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1;
+ }
+
+ /** Make a copy of the supplied string, replacing the original pointer */
+ SI_Error CopyString(const SI_CHAR *& a_pString);
+
+ /** Delete a string from the copied strings buffer if necessary */
+ void DeleteString(const SI_CHAR * a_pString);
+
+ /** Internal use of our string comparison function */
+ bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const {
+ const static SI_STRLESS isLess = SI_STRLESS();
+ return isLess(a_pLeft, a_pRight);
+ }
+
+ bool IsMultiLineTag(const SI_CHAR * a_pData) const;
+ bool IsMultiLineData(const SI_CHAR * a_pData) const;
+ bool LoadMultiLineText(
+ SI_CHAR *& a_pData,
+ const SI_CHAR *& a_pVal,
+ const SI_CHAR * a_pTagName,
+ bool a_bAllowBlankLinesInComment = false
+ ) const;
+ bool IsNewLineChar(SI_CHAR a_c) const;
+
+ bool OutputMultiLineText(
+ OutputWriter & a_oOutput,
+ Converter & a_oConverter,
+ const SI_CHAR * a_pText
+ ) const;
+
+private:
+ /** Copy of the INI file data in our character format. This will be
+ modified when parsed to have NULL characters added after all
+ interesting string entries. All of the string pointers to sections,
+ keys and values point into this block of memory.
+ */
+ SI_CHAR * m_pData;
+
+ /** Length of the data that we have stored. Used when deleting strings
+ to determine if the string is stored here or in the allocated string
+ buffer.
+ */
+ size_t m_uDataLen;
+
+ /** File comment for this data, if one exists. */
+ const SI_CHAR * m_pFileComment;
+
+ /** Parsed INI data. Section -> (Key -> Value). */
+ TSection m_data;
+
+ /** This vector stores allocated memory for copies of strings that have
+ been supplied after the file load. It will be empty unless SetValue()
+ has been called.
+ */
+ TNamesDepend m_strings;
+
+ /** Is the format of our datafile UTF-8 or MBCS? */
+ bool m_bStoreIsUtf8;
+
+ /** Are multiple values permitted for the same key? */
+ bool m_bAllowMultiKey;
+
+ /** Are data values permitted to span multiple lines? */
+ bool m_bAllowMultiLine;
+
+ /** Next order value, used to ensure sections and keys are output in the
+ same order that they are loaded/added.
+ */
+ int m_nOrder;
+};
+
+// ---------------------------------------------------------------------------
+// IMPLEMENTATION
+// ---------------------------------------------------------------------------
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl(
+ bool a_bIsUtf8,
+ bool a_bAllowMultiKey,
+ bool a_bAllowMultiLine
+ )
+ : m_pData(0)
+ , m_uDataLen(0)
+ , m_pFileComment(NULL)
+ , m_bStoreIsUtf8(a_bIsUtf8)
+ , m_bAllowMultiKey(a_bAllowMultiKey)
+ , m_bAllowMultiLine(a_bAllowMultiLine)
+ , m_nOrder(0)
+{ }
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::~CSimpleIniTempl()
+{
+ Reset();
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+void
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Reset()
+{
+ // remove all data
+ delete[] m_pData;
+ m_pData = NULL;
+ m_uDataLen = 0;
+ m_pFileComment = NULL;
+ if (!m_data.empty()) {
+ m_data.erase(m_data.begin(), m_data.end());
+ }
+
+ // remove all strings
+ if (!m_strings.empty()) {
+ typename TNamesDepend::iterator i = m_strings.begin();
+ for (; i != m_strings.end(); ++i) {
+ delete[] const_cast<SI_CHAR*>(i->pItem);
+ }
+ m_strings.erase(m_strings.begin(), m_strings.end());
+ }
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
+ const char * a_pszFile
+ )
+{
+ FILE * fp = NULL;
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ fopen_s(&fp, a_pszFile, "rb");
+#else // !__STDC_WANT_SECURE_LIB__
+ fp = fopen(a_pszFile, "rb");
+#endif // __STDC_WANT_SECURE_LIB__
+ if (!fp) {
+ return SI_FILE;
+ }
+ SI_Error rc = LoadFile(fp);
+ fclose(fp);
+ return rc;
+}
+
+#ifdef SI_HAS_WIDE_FILE
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
+ const SI_WCHAR_T * a_pwszFile
+ )
+{
+#ifdef _WIN32
+ FILE * fp = NULL;
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ _wfopen_s(&fp, a_pwszFile, L"rb");
+#else // !__STDC_WANT_SECURE_LIB__
+ fp = _wfopen(a_pwszFile, L"rb");
+#endif // __STDC_WANT_SECURE_LIB__
+ if (!fp) return SI_FILE;
+ SI_Error rc = LoadFile(fp);
+ fclose(fp);
+ return rc;
+#else // !_WIN32 (therefore SI_CONVERT_ICU)
+ char szFile[256];
+ u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
+ return LoadFile(szFile);
+#endif // _WIN32
+}
+#endif // SI_HAS_WIDE_FILE
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
+ FILE * a_fpFile
+ )
+{
+ // load the raw file data
+ int retval = fseek(a_fpFile, 0, SEEK_END);
+ if (retval != 0) {
+ return SI_FILE;
+ }
+ long lSize = ftell(a_fpFile);
+ if (lSize < 0) {
+ return SI_FILE;
+ }
+ if (lSize == 0) {
+ return SI_OK;
+ }
+ char * pData = new char[lSize];
+ if (!pData) {
+ return SI_NOMEM;
+ }
+ fseek(a_fpFile, 0, SEEK_SET);
+ size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
+ if (uRead != (size_t) lSize) {
+ delete[] pData;
+ return SI_FILE;
+ }
+
+ // convert the raw data to unicode
+ SI_Error rc = Load(pData, uRead);
+ delete[] pData;
+ return rc;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Load(
+ const char * a_pData,
+ size_t a_uDataLen
+ )
+{
+ SI_CONVERTER converter(m_bStoreIsUtf8);
+
+ if (a_uDataLen == 0) {
+ return SI_OK;
+ }
+
+ // consume the UTF-8 BOM if it exists
+ if (m_bStoreIsUtf8 && a_uDataLen >= 3) {
+ if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
+ a_pData += 3;
+ a_uDataLen -= 3;
+ }
+ }
+
+ // determine the length of the converted data
+ size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
+ if (uLen == (size_t)(-1)) {
+ return SI_FAIL;
+ }
+
+ // allocate memory for the data, ensure that there is a NULL
+ // terminator wherever the converted data ends
+ SI_CHAR * pData = new SI_CHAR[uLen+1];
+ if (!pData) {
+ return SI_NOMEM;
+ }
+ memset(pData, 0, sizeof(SI_CHAR)*(uLen+1));
+
+ // convert the data
+ if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
+ delete[] pData;
+ return SI_FAIL;
+ }
+
+ // parse it
+ const static SI_CHAR empty = 0;
+ SI_CHAR * pWork = pData;
+ const SI_CHAR * pSection = ∅
+ const SI_CHAR * pItem = NULL;
+ const SI_CHAR * pVal = NULL;
+ const SI_CHAR * pComment = NULL;
+
+ // We copy the strings if we are loading data into this class when we
+ // already have stored some.
+ bool bCopyStrings = (m_pData != NULL);
+
+ // find a file comment if it exists, this is a comment that starts at the
+ // beginning of the file and continues until the first blank line.
+ SI_Error rc = FindFileComment(pWork, bCopyStrings);
+ if (rc < 0) return rc;
+
+ // add every entry in the file to the data table
+ while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
+ rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings);
+ if (rc < 0) return rc;
+ }
+
+ // store these strings if we didn't copy them
+ if (bCopyStrings) {
+ delete[] pData;
+ }
+ else {
+ m_pData = pData;
+ m_uDataLen = uLen+1;
+ }
+
+ return SI_OK;
+}
+
+#ifdef SI_SUPPORT_IOSTREAMS
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Load(
+ std::istream & a_istream
+ )
+{
+ std::string strData;
+ char szBuf[512];
+ do {
+ a_istream.get(szBuf, sizeof(szBuf), '\0');
+ strData.append(szBuf);
+ }
+ while (a_istream.good());
+ return Load(strData);
+}
+#endif // SI_SUPPORT_IOSTREAMS
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindFileComment(
+ SI_CHAR *& a_pData,
+ bool a_bCopyStrings
+ )
+{
+ // there can only be a single file comment
+ if (m_pFileComment) {
+ return SI_OK;
+ }
+
+ // Load the file comment as multi-line text, this will modify all of
+ // the newline characters to be single \n chars
+ if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
+ return SI_OK;
+ }
+
+ // copy the string if necessary
+ if (a_bCopyStrings) {
+ SI_Error rc = CopyString(m_pFileComment);
+ if (rc < 0) return rc;
+ }
+
+ return SI_OK;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
+ SI_CHAR *& a_pData,
+ const SI_CHAR *& a_pSection,
+ const SI_CHAR *& a_pKey,
+ const SI_CHAR *& a_pVal,
+ const SI_CHAR *& a_pComment
+ ) const
+{
+ a_pComment = NULL;
+
+ SI_CHAR * pTrail = NULL;
+ while (*a_pData) {
+ // skip spaces and empty lines
+ while (*a_pData && IsSpace(*a_pData)) {
+ ++a_pData;
+ }
+ if (!*a_pData) {
+ break;
+ }
+
+ // skip processing of comment lines but keep a pointer to
+ // the start of the comment.
+ if (IsComment(*a_pData)) {
+ LoadMultiLineText(a_pData, a_pComment, NULL, true);
+ continue;
+ }
+
+ // process section names
+ if (*a_pData == '[') {
+ // skip leading spaces
+ ++a_pData;
+ while (*a_pData && IsSpace(*a_pData)) {
+ ++a_pData;
+ }
+
+ // find the end of the section name (it may contain spaces)
+ // and convert it to lowercase as necessary
+ a_pSection = a_pData;
+ while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ // if it's an invalid line, just skip it
+ if (*a_pData != ']') {
+ continue;
+ }
+
+ // remove trailing spaces from the section
+ pTrail = a_pData - 1;
+ while (pTrail >= a_pSection && IsSpace(*pTrail)) {
+ --pTrail;
+ }
+ ++pTrail;
+ *pTrail = 0;
+
+ // skip to the end of the line
+ ++a_pData; // safe as checked that it == ']' above
+ while (*a_pData && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ a_pKey = NULL;
+ a_pVal = NULL;
+ return true;
+ }
+
+ // find the end of the key name (it may contain spaces)
+ // and convert it to lowercase as necessary
+ a_pKey = a_pData;
+ while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ // if it's an invalid line, just skip it
+ if (*a_pData != '=') {
+ continue;
+ }
+
+ // empty keys are invalid
+ if (a_pKey == a_pData) {
+ while (*a_pData && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+ continue;
+ }
+
+ // remove trailing spaces from the key
+ pTrail = a_pData - 1;
+ while (pTrail >= a_pKey && IsSpace(*pTrail)) {
+ --pTrail;
+ }
+ ++pTrail;
+ *pTrail = 0;
+
+ // skip leading whitespace on the value
+ ++a_pData; // safe as checked that it == '=' above
+ while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
+ ++a_pData;
+ }
+
+ // find the end of the value which is the end of this line
+ a_pVal = a_pData;
+ while (*a_pData && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ // remove trailing spaces from the value
+ pTrail = a_pData - 1;
+ if (*a_pData) { // prepare for the next round
+ SkipNewLine(a_pData);
+ }
+ while (pTrail >= a_pVal && IsSpace(*pTrail)) {
+ --pTrail;
+ }
+ ++pTrail;
+ *pTrail = 0;
+
+ // check for multi-line entries
+ if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
+ // skip the "<<<" to get the tag that will end the multiline
+ const SI_CHAR * pTagName = a_pVal + 3;
+ return LoadMultiLineText(a_pData, a_pVal, pTagName);
+ }
+
+ // return the standard entry
+ return true;
+ }
+
+ return false;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineTag(
+ const SI_CHAR * a_pVal
+ ) const
+{
+ // check for the "<<<" prefix for a multi-line entry
+ if (*a_pVal++ != '<') return false;
+ if (*a_pVal++ != '<') return false;
+ if (*a_pVal++ != '<') return false;
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData(
+ const SI_CHAR * a_pData
+ ) const
+{
+ // data is multi-line if it has any of the following features:
+ // * whitespace prefix
+ // * embedded newlines
+ // * whitespace suffix
+
+ // empty string
+ if (!*a_pData) {
+ return false;
+ }
+
+ // check for prefix
+ if (IsSpace(*a_pData)) {
+ return true;
+ }
+
+ // embedded newlines
+ while (*a_pData) {
+ if (IsNewLineChar(*a_pData)) {
+ return true;
+ }
+ ++a_pData;
+ }
+
+ // check for suffix
+ if (IsSpace(*--a_pData)) {
+ return true;
+ }
+
+ return false;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar(
+ SI_CHAR a_c
+ ) const
+{
+ return (a_c == '\n' || a_c == '\r');
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
+ SI_CHAR *& a_pData,
+ const SI_CHAR *& a_pVal,
+ const SI_CHAR * a_pTagName,
+ bool a_bAllowBlankLinesInComment
+ ) const
+{
+ // we modify this data to strip all newlines down to a single '\n'
+ // character. This means that on Windows we need to strip out some
+ // characters which will make the data shorter.
+ // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
+ // LINE1-LINE1\nLINE2-LINE2\0
+ // The pDataLine entry is the pointer to the location in memory that
+ // the current line needs to start to run following the existing one.
+ // This may be the same as pCurrLine in which case no move is needed.
+ SI_CHAR * pDataLine = a_pData;
+ SI_CHAR * pCurrLine;
+
+ // value starts at the current line
+ a_pVal = a_pData;
+
+ // find the end tag. This tag must start in column 1 and be
+ // followed by a newline. No whitespace removal is done while
+ // searching for this tag.
+ SI_CHAR cEndOfLineChar = *a_pData;
+ for(;;) {
+ // if we are loading comments then we need a comment character as
+ // the first character on every line
+ if (!a_pTagName && !IsComment(*a_pData)) {
+ // if we aren't allowing blank lines then we're done
+ if (!a_bAllowBlankLinesInComment) {
+ break;
+ }
+
+ // if we are allowing blank lines then we only include them
+ // in this comment if another comment follows, so read ahead
+ // to find out.
+ SI_CHAR * pCurr = a_pData;
+ int nNewLines = 0;
+ while (IsSpace(*pCurr)) {
+ if (IsNewLineChar(*pCurr)) {
+ ++nNewLines;
+ SkipNewLine(pCurr);
+ }
+ else {
+ ++pCurr;
+ }
+ }
+
+ // we have a comment, add the blank lines to the output
+ // and continue processing from here
+ if (IsComment(*pCurr)) {
+ for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n';
+ a_pData = pCurr;
+ continue;
+ }
+
+ // the comment ends here
+ break;
+ }
+
+ // find the end of this line
+ pCurrLine = a_pData;
+ while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
+
+ // move this line down to the location that it should be if necessary
+ if (pDataLine < pCurrLine) {
+ size_t nLen = (size_t) (a_pData - pCurrLine);
+ memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
+ pDataLine[nLen] = '\0';
+ }
+
+ // end the line with a NULL
+ cEndOfLineChar = *a_pData;
+ *a_pData = 0;
+
+ // if are looking for a tag then do the check now. This is done before
+ // checking for end of the data, so that if we have the tag at the end
+ // of the data then the tag is removed correctly.
+ if (a_pTagName &&
+ (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)))
+ {
+ break;
+ }
+
+ // if we are at the end of the data then we just automatically end
+ // this entry and return the current data.
+ if (!cEndOfLineChar) {
+ return true;
+ }
+
+ // otherwise we need to process this newline to ensure that it consists
+ // of just a single \n character.
+ pDataLine += (a_pData - pCurrLine);
+ *a_pData = cEndOfLineChar;
+ SkipNewLine(a_pData);
+ *pDataLine++ = '\n';
+ }
+
+ // if we didn't find a comment at all then return false
+ if (a_pVal == a_pData) {
+ a_pVal = NULL;
+ return false;
+ }
+
+ // the data (which ends at the end of the last line) needs to be
+ // null-terminated BEFORE before the newline character(s). If the
+ // user wants a new line in the multi-line data then they need to
+ // add an empty line before the tag.
+ *--pDataLine = '\0';
+
+ // if looking for a tag and if we aren't at the end of the data,
+ // then move a_pData to the start of the next line.
+ if (a_pTagName && cEndOfLineChar) {
+ SI_ASSERT(IsNewLineChar(cEndOfLineChar));
+ *a_pData = cEndOfLineChar;
+ SkipNewLine(a_pData);
+ }
+
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString(
+ const SI_CHAR *& a_pString
+ )
+{
+ size_t uLen = 0;
+ if (sizeof(SI_CHAR) == sizeof(char)) {
+ uLen = strlen((const char *)a_pString);
+ }
+ else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
+ uLen = wcslen((const wchar_t *)a_pString);
+ }
+ else {
+ for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
+ }
+ ++uLen; // NULL character
+ SI_CHAR * pCopy = new SI_CHAR[uLen];
+ if (!pCopy) {
+ return SI_NOMEM;
+ }
+ memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
+ m_strings.push_back(pCopy);
+ a_pString = pCopy;
+ return SI_OK;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pValue,
+ const SI_CHAR * a_pComment,
+ bool a_bForceReplace,
+ bool a_bCopyStrings
+ )
+{
+ SI_Error rc;
+ bool bInserted = false;
+
+ SI_ASSERT(!a_pComment || IsComment(*a_pComment));
+
+ // if we are copying strings then make a copy of the comment now
+ // because we will need it when we add the entry.
+ if (a_bCopyStrings && a_pComment) {
+ rc = CopyString(a_pComment);
+ if (rc < 0) return rc;
+ }
+
+ // create the section entry if necessary
+ typename TSection::iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ // if the section doesn't exist then we need a copy as the
+ // string needs to last beyond the end of this function
+ if (a_bCopyStrings) {
+ rc = CopyString(a_pSection);
+ if (rc < 0) return rc;
+ }
+
+ // only set the comment if this is a section only entry
+ Entry oSection(a_pSection, ++m_nOrder);
+ if (a_pComment && (!a_pKey || !a_pValue)) {
+ oSection.pComment = a_pComment;
+ }
+
+ typename TSection::value_type oEntry(oSection, TKeyVal());
+ typedef typename TSection::iterator SectionIterator;
+ std::pair<SectionIterator,bool> i = m_data.insert(oEntry);
+ iSection = i.first;
+ bInserted = true;
+ }
+ if (!a_pKey || !a_pValue) {
+ // section only entries are specified with pItem and pVal as NULL
+ return bInserted ? SI_INSERTED : SI_UPDATED;
+ }
+
+ // check for existence of the key
+ TKeyVal & keyval = iSection->second;
+ typename TKeyVal::iterator iKey = keyval.find(a_pKey);
+
+ // remove all existing entries but save the load order and
+ // comment of the first entry
+ int nLoadOrder = ++m_nOrder;
+ if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) {
+ const SI_CHAR * pComment = NULL;
+ while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) {
+ if (iKey->first.nOrder < nLoadOrder) {
+ nLoadOrder = iKey->first.nOrder;
+ pComment = iKey->first.pComment;
+ }
+ ++iKey;
+ }
+ if (pComment) {
+ DeleteString(a_pComment);
+ a_pComment = pComment;
+ CopyString(a_pComment);
+ }
+ Delete(a_pSection, a_pKey);
+ iKey = keyval.end();
+ }
+
+ // make string copies if necessary
+ bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
+ if (a_bCopyStrings) {
+ if (bForceCreateNewKey || iKey == keyval.end()) {
+ // if the key doesn't exist then we need a copy as the
+ // string needs to last beyond the end of this function
+ // because we will be inserting the key next
+ rc = CopyString(a_pKey);
+ if (rc < 0) return rc;
+ }
+
+ // we always need a copy of the value
+ rc = CopyString(a_pValue);
+ if (rc < 0) return rc;
+ }
+
+ // create the key entry
+ if (iKey == keyval.end() || bForceCreateNewKey) {
+ Entry oKey(a_pKey, nLoadOrder);
+ if (a_pComment) {
+ oKey.pComment = a_pComment;
+ }
+ typename TKeyVal::value_type oEntry(oKey, NULL);
+ iKey = keyval.insert(oEntry);
+ bInserted = true;
+ }
+ iKey->second = a_pValue;
+ return bInserted ? SI_INSERTED : SI_UPDATED;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+const SI_CHAR *
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pDefault,
+ bool * a_pHasMultiple
+ ) const
+{
+ if (a_pHasMultiple) {
+ *a_pHasMultiple = false;
+ }
+ if (!a_pSection || !a_pKey) {
+ return a_pDefault;
+ }
+ typename TSection::const_iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return a_pDefault;
+ }
+ typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
+ if (iKeyVal == iSection->second.end()) {
+ return a_pDefault;
+ }
+
+ // check for multiple entries with the same key
+ if (m_bAllowMultiKey && a_pHasMultiple) {
+ typename TKeyVal::const_iterator iTemp = iKeyVal;
+ if (++iTemp != iSection->second.end()) {
+ if (!IsLess(a_pKey, iTemp->first.pItem)) {
+ *a_pHasMultiple = true;
+ }
+ }
+ }
+
+ return iKeyVal->second;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+long
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetLongValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ long a_nDefault,
+ bool * a_pHasMultiple
+ ) const
+{
+ // return the default if we don't have a value
+ const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
+ if (!pszValue || !*pszValue) return a_nDefault;
+
+ // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
+ char szValue[64] = { 0 };
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
+ return a_nDefault;
+ }
+
+ // handle the value as hex if prefaced with "0x"
+ long nValue = a_nDefault;
+ char * pszSuffix = szValue;
+ if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
+ if (!szValue[2]) return a_nDefault;
+ nValue = strtol(&szValue[2], &pszSuffix, 16);
+ }
+ else {
+ nValue = strtol(szValue, &pszSuffix, 10);
+ }
+
+ // any invalid strings will return the default value
+ if (*pszSuffix) {
+ return a_nDefault;
+ }
+
+ return nValue;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ long a_nValue,
+ const SI_CHAR * a_pComment,
+ bool a_bUseHex,
+ bool a_bForceReplace
+ )
+{
+ // use SetValue to create sections
+ if (!a_pSection || !a_pKey) return SI_FAIL;
+
+ // convert to an ASCII string
+ char szInput[64];
+ sprintf(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
+
+ // convert to output text
+ SI_CHAR szOutput[64];
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ c.ConvertFromStore(szInput, strlen(szInput) + 1,
+ szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
+
+ // actually add it
+ return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetBoolValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bDefault,
+ bool * a_pHasMultiple
+ ) const
+{
+ // return the default if we don't have a value
+ const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
+ if (!pszValue || !*pszValue) return a_bDefault;
+
+ // we only look at the minimum number of characters
+ switch (pszValue[0]) {
+ case 't': case 'T': // true
+ case 'y': case 'Y': // yes
+ case '1': // 1 (one)
+ return true;
+
+ case 'f': case 'F': // false
+ case 'n': case 'N': // no
+ case '0': // 0 (zero)
+ return false;
+
+ case 'o': case 'O':
+ if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on
+ if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off
+ break;
+ }
+
+ // no recognized value, return the default
+ return a_bDefault;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetBoolValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bValue,
+ const SI_CHAR * a_pComment,
+ bool a_bForceReplace
+ )
+{
+ // use SetValue to create sections
+ if (!a_pSection || !a_pKey) return SI_FAIL;
+
+ // convert to an ASCII string
+ const char * pszInput = a_bValue ? "true" : "false";
+
+ // convert to output text
+ SI_CHAR szOutput[64];
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ c.ConvertFromStore(pszInput, strlen(pszInput) + 1,
+ szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
+
+ // actually add it
+ return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ TNamesDepend & a_values
+ ) const
+{
+ a_values.clear();
+
+ if (!a_pSection || !a_pKey) {
+ return false;
+ }
+ typename TSection::const_iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return false;
+ }
+ typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
+ if (iKeyVal == iSection->second.end()) {
+ return false;
+ }
+
+ // insert all values for this key
+ a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
+ if (m_bAllowMultiKey) {
+ ++iKeyVal;
+ while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {
+ a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
+ ++iKeyVal;
+ }
+ }
+
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+int
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSectionSize(
+ const SI_CHAR * a_pSection
+ ) const
+{
+ if (!a_pSection) {
+ return -1;
+ }
+
+ typename TSection::const_iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return -1;
+ }
+ const TKeyVal & section = iSection->second;
+
+ // if multi-key isn't permitted then the section size is
+ // the number of keys that we have.
+ if (!m_bAllowMultiKey || section.empty()) {
+ return (int) section.size();
+ }
+
+ // otherwise we need to count them
+ int nCount = 0;
+ const SI_CHAR * pLastKey = NULL;
+ typename TKeyVal::const_iterator iKeyVal = section.begin();
+ for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
+ if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
+ ++nCount;
+ pLastKey = iKeyVal->first.pItem;
+ }
+ }
+ return nCount;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+const typename CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::TKeyVal *
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSection(
+ const SI_CHAR * a_pSection
+ ) const
+{
+ if (a_pSection) {
+ typename TSection::const_iterator i = m_data.find(a_pSection);
+ if (i != m_data.end()) {
+ return &(i->second);
+ }
+ }
+ return 0;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+void
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections(
+ TNamesDepend & a_names
+ ) const
+{
+ a_names.clear();
+ typename TSection::const_iterator i = m_data.begin();
+ for (int n = 0; i != m_data.end(); ++i, ++n ) {
+ a_names.push_back(i->first);
+ }
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllKeys(
+ const SI_CHAR * a_pSection,
+ TNamesDepend & a_names
+ ) const
+{
+ a_names.clear();
+
+ if (!a_pSection) {
+ return false;
+ }
+
+ typename TSection::const_iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return false;
+ }
+
+ const TKeyVal & section = iSection->second;
+ const SI_CHAR * pLastKey = NULL;
+ typename TKeyVal::const_iterator iKeyVal = section.begin();
+ for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) {
+ if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
+ a_names.push_back(iKeyVal->first);
+ pLastKey = iKeyVal->first.pItem;
+ }
+ }
+
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
+ const char * a_pszFile,
+ bool a_bAddSignature
+ ) const
+{
+ FILE * fp = NULL;
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ fopen_s(&fp, a_pszFile, "wb");
+#else // !__STDC_WANT_SECURE_LIB__
+ fp = fopen(a_pszFile, "wb");
+#endif // __STDC_WANT_SECURE_LIB__
+ if (!fp) return SI_FILE;
+ SI_Error rc = SaveFile(fp, a_bAddSignature);
+ fclose(fp);
+ return rc;
+}
+
+#ifdef SI_HAS_WIDE_FILE
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
+ const SI_WCHAR_T * a_pwszFile,
+ bool a_bAddSignature
+ ) const
+{
+#ifdef _WIN32
+ FILE * fp = _wfopen(a_pwszFile, L"wb");
+ if (!fp) return SI_FILE;
+ SI_Error rc = SaveFile(fp, a_bAddSignature);
+ fclose(fp);
+ return rc;
+#else // !_WIN32 (therefore SI_CONVERT_ICU)
+ char szFile[256];
+ u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
+ return SaveFile(szFile, a_bAddSignature);
+#endif // _WIN32
+}
+#endif // SI_HAS_WIDE_FILE
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
+ FILE * a_pFile,
+ bool a_bAddSignature
+ ) const
+{
+ FileWriter writer(a_pFile);
+ return Save(writer, a_bAddSignature);
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
+ OutputWriter & a_oOutput,
+ bool a_bAddSignature
+ ) const
+{
+ Converter convert(m_bStoreIsUtf8);
+
+ // add the UTF-8 signature if it is desired
+ if (m_bStoreIsUtf8 && a_bAddSignature) {
+ a_oOutput.Write(SI_UTF8_SIGNATURE);
+ }
+
+ // get all of the sections sorted in load order
+ TNamesDepend oSections;
+ GetAllSections(oSections);
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+ oSections.sort();
+#elif defined(__BORLANDC__)
+ oSections.sort(Entry::LoadOrder());
+#else
+ oSections.sort(typename Entry::LoadOrder());
+#endif
+
+ // write the file comment if we have one
+ bool bNeedNewLine = false;
+ if (m_pFileComment) {
+ if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
+ return SI_FAIL;
+ }
+ bNeedNewLine = true;
+ }
+
+ // iterate through our sections and output the data
+ typename TNamesDepend::const_iterator iSection = oSections.begin();
+ for ( ; iSection != oSections.end(); ++iSection ) {
+ // write out the comment if there is one
+ if (iSection->pComment) {
+ if (bNeedNewLine) {
+ a_oOutput.Write(SI_NEWLINE_A);
+ a_oOutput.Write(SI_NEWLINE_A);
+ }
+ if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) {
+ return SI_FAIL;
+ }
+ bNeedNewLine = false;
+ }
+
+ if (bNeedNewLine) {
+ a_oOutput.Write(SI_NEWLINE_A);
+ a_oOutput.Write(SI_NEWLINE_A);
+ bNeedNewLine = false;
+ }
+
+ // write the section (unless there is no section name)
+ if (*iSection->pItem) {
+ if (!convert.ConvertToStore(iSection->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write("[");
+ a_oOutput.Write(convert.Data());
+ a_oOutput.Write("]");
+ a_oOutput.Write(SI_NEWLINE_A);
+ }
+
+ // get all of the keys sorted in load order
+ TNamesDepend oKeys;
+ GetAllKeys(iSection->pItem, oKeys);
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+ oKeys.sort();
+#elif defined(__BORLANDC__)
+ oKeys.sort(Entry::LoadOrder());
+#else
+ oKeys.sort(typename Entry::LoadOrder());
+#endif
+
+ // write all keys and values
+ typename TNamesDepend::const_iterator iKey = oKeys.begin();
+ for ( ; iKey != oKeys.end(); ++iKey) {
+ // get all values for this key
+ TNamesDepend oValues;
+ GetAllValues(iSection->pItem, iKey->pItem, oValues);
+
+ typename TNamesDepend::const_iterator iValue = oValues.begin();
+ for ( ; iValue != oValues.end(); ++iValue) {
+ // write out the comment if there is one
+ if (iValue->pComment) {
+ a_oOutput.Write(SI_NEWLINE_A);
+ if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) {
+ return SI_FAIL;
+ }
+ }
+
+ // write the key
+ if (!convert.ConvertToStore(iKey->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write(convert.Data());
+
+ // write the value
+ if (!convert.ConvertToStore(iValue->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write(" = ");
+ if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
+ // multi-line data needs to be processed specially to ensure
+ // that we use the correct newline format for the current system
+ a_oOutput.Write("<<<SI-END-OF-MULTILINE-TEXT" SI_NEWLINE_A);
+ if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write("SI-END-OF-MULTILINE-TEXT");
+ }
+ else {
+ a_oOutput.Write(convert.Data());
+ }
+ a_oOutput.Write(SI_NEWLINE_A);
+ }
+ }
+
+ bNeedNewLine = true;
+ }
+
+ return SI_OK;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::OutputMultiLineText(
+ OutputWriter & a_oOutput,
+ Converter & a_oConverter,
+ const SI_CHAR * a_pText
+ ) const
+{
+ const SI_CHAR * pEndOfLine;
+ SI_CHAR cEndOfLineChar = *a_pText;
+ while (cEndOfLineChar) {
+ // find the end of this line
+ pEndOfLine = a_pText;
+ for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;
+ cEndOfLineChar = *pEndOfLine;
+
+ // temporarily null terminate, convert and output the line
+ *const_cast<SI_CHAR*>(pEndOfLine) = 0;
+ if (!a_oConverter.ConvertToStore(a_pText)) {
+ return false;
+ }
+ *const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar;
+ a_pText += (pEndOfLine - a_pText) + 1;
+ a_oOutput.Write(a_oConverter.Data());
+ a_oOutput.Write(SI_NEWLINE_A);
+ }
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bRemoveEmpty
+ )
+{
+ if (!a_pSection) {
+ return false;
+ }
+
+ typename TSection::iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return false;
+ }
+
+ // remove a single key if we have a keyname
+ if (a_pKey) {
+ typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
+ if (iKeyVal == iSection->second.end()) {
+ return false;
+ }
+
+ // remove any copied strings and then the key
+ typename TKeyVal::iterator iDelete;
+ do {
+ iDelete = iKeyVal++;
+
+ DeleteString(iDelete->first.pItem);
+ DeleteString(iDelete->second);
+ iSection->second.erase(iDelete);
+ }
+ while (iKeyVal != iSection->second.end()
+ && !IsLess(a_pKey, iKeyVal->first.pItem));
+
+ // done now if the section is not empty or we are not pruning away
+ // the empty sections. Otherwise let it fall through into the section
+ // deletion code
+ if (!a_bRemoveEmpty || !iSection->second.empty()) {
+ return true;
+ }
+ }
+ else {
+ // delete all copied strings from this section. The actual
+ // entries will be removed when the section is removed.
+ typename TKeyVal::iterator iKeyVal = iSection->second.begin();
+ for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
+ DeleteString(iKeyVal->first.pItem);
+ DeleteString(iKeyVal->second);
+ }
+ }
+
+ // delete the section itself
+ DeleteString(iSection->first.pItem);
+ m_data.erase(iSection);
+
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+void
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString(
+ const SI_CHAR * a_pString
+ )
+{
+ // strings may exist either inside the data block, or they will be
+ // individually allocated and stored in m_strings. We only physically
+ // delete those stored in m_strings.
+ if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
+ typename TNamesDepend::iterator i = m_strings.begin();
+ for (;i != m_strings.end(); ++i) {
+ if (a_pString == i->pItem) {
+ delete[] const_cast<SI_CHAR*>(i->pItem);
+ m_strings.erase(i);
+ break;
+ }
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+// CONVERSION FUNCTIONS
+// ---------------------------------------------------------------------------
+
+// Defines the conversion classes for different libraries. Before including
+// SimpleIni.h, set the converter that you wish you use by defining one of the
+// following symbols.
+//
+// SI_CONVERT_GENERIC Use the Unicode reference conversion library in
+// the accompanying files ConvertUTF.h/c
+// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
+// ICU headers on include path and icuuc.lib
+// SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
+
+#if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
+# ifdef _WIN32
+# define SI_CONVERT_WIN32
+# else
+# define SI_CONVERT_GENERIC
+# endif
+#endif
+
+/**
+ * Generic case-sensitive less than comparison. This class returns numerically
+ * ordered ASCII case-sensitive text for all possible sizes and types of
+ * SI_CHAR.
+ */
+template<class SI_CHAR>
+struct SI_GenericCase {
+ bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
+ long cmp;
+ for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
+ cmp = (long) *pLeft - (long) *pRight;
+ if (cmp != 0) {
+ return cmp < 0;
+ }
+ }
+ return *pRight != 0;
+ }
+};
+
+/**
+ * Generic ASCII case-insensitive less than comparison. This class returns
+ * numerically ordered ASCII case-insensitive text for all possible sizes
+ * and types of SI_CHAR. It is not safe for MBCS text comparison where
+ * ASCII A-Z characters are used in the encoding of multi-byte characters.
+ */
+template<class SI_CHAR>
+struct SI_GenericNoCase {
+ inline SI_CHAR locase(SI_CHAR ch) const {
+ return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
+ }
+ bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
+ long cmp;
+ for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
+ cmp = (long) locase(*pLeft) - (long) locase(*pRight);
+ if (cmp != 0) {
+ return cmp < 0;
+ }
+ }
+ return *pRight != 0;
+ }
+};
+
+/**
+ * Null conversion class for MBCS/UTF-8 to char (or equivalent).
+ */
+template<class SI_CHAR>
+class SI_ConvertA {
+ bool m_bStoreIsUtf8;
+protected:
+ SI_ConvertA() { }
+public:
+ SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
+
+ /* copy and assignment */
+ SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }
+ SI_ConvertA & operator=(const SI_ConvertA & rhs) {
+ m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
+ return *this;
+ }
+
+ /** Calculate the number of SI_CHAR required for converting the input
+ * from the storage format. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @return Number of SI_CHAR required by the string when
+ * converted. If there are embedded NULL bytes in the
+ * input data, only the string up and not including
+ * the NULL byte will be converted.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen)
+ {
+ (void)a_pInputData;
+ SI_ASSERT(a_uInputDataLen != (size_t) -1);
+
+ // ASCII/MBCS/UTF-8 needs no conversion
+ return a_uInputDataLen;
+ }
+
+ /** Convert the input string from the storage format to SI_CHAR.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @param a_pOutputData Pointer to the output buffer to received the
+ * converted data.
+ * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
+ * @return true if all of the input data was successfully
+ * converted.
+ */
+ bool ConvertFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen,
+ SI_CHAR * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ // ASCII/MBCS/UTF-8 needs no conversion
+ if (a_uInputDataLen > a_uOutputDataSize) {
+ return false;
+ }
+ memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
+ return true;
+ }
+
+ /** Calculate the number of char required by the storage format of this
+ * data. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated string to calculate the number of
+ * bytes required to be converted to storage format.
+ * @return Number of bytes required by the string when
+ * converted to storage format. This size always
+ * includes space for the terminating NULL character.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeToStore(
+ const SI_CHAR * a_pInputData)
+ {
+ // ASCII/MBCS/UTF-8 needs no conversion
+ return strlen((const char *)a_pInputData) + 1;
+ }
+
+ /** Convert the input string to the storage format of this data.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated source string to convert. All of
+ * the data will be converted including the
+ * terminating NULL character.
+ * @param a_pOutputData Pointer to the buffer to receive the converted
+ * string.
+ * @param a_uOutputDataSize Size of the output buffer in char.
+ * @return true if all of the input data, including the
+ * terminating NULL character was successfully
+ * converted.
+ */
+ bool ConvertToStore(
+ const SI_CHAR * a_pInputData,
+ char * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ // calc input string length (SI_CHAR type and size independent)
+ size_t uInputLen = strlen((const char *)a_pInputData) + 1;
+ if (uInputLen > a_uOutputDataSize) {
+ return false;
+ }
+
+ // ascii/UTF-8 needs no conversion
+ memcpy(a_pOutputData, a_pInputData, uInputLen);
+ return true;
+ }
+};
+
+
+// ---------------------------------------------------------------------------
+// SI_CONVERT_GENERIC
+// ---------------------------------------------------------------------------
+#ifdef SI_CONVERT_GENERIC
+
+#define SI_Case SI_GenericCase
+#define SI_NoCase SI_GenericNoCase
+
+#include <wchar.h>
+#include "ConvertUTF.h"
+
+/**
+ * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference
+ * library functions. This can be used on all platforms.
+ */
+template<class SI_CHAR>
+class SI_ConvertW {
+ bool m_bStoreIsUtf8;
+protected:
+ SI_ConvertW() { }
+public:
+ SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
+
+ /* copy and assignment */
+ SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
+ SI_ConvertW & operator=(const SI_ConvertW & rhs) {
+ m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
+ return *this;
+ }
+
+ /** Calculate the number of SI_CHAR required for converting the input
+ * from the storage format. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @return Number of SI_CHAR required by the string when
+ * converted. If there are embedded NULL bytes in the
+ * input data, only the string up and not including
+ * the NULL byte will be converted.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen)
+ {
+ SI_ASSERT(a_uInputDataLen != (size_t) -1);
+
+ if (m_bStoreIsUtf8) {
+ // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
+ // so we just return the same number of characters required as for
+ // the source text.
+ return a_uInputDataLen;
+ }
+ else {
+ return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
+ }
+ }
+
+ /** Convert the input string from the storage format to SI_CHAR.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @param a_pOutputData Pointer to the output buffer to received the
+ * converted data.
+ * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
+ * @return true if all of the input data was successfully
+ * converted.
+ */
+ bool ConvertFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen,
+ SI_CHAR * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ if (m_bStoreIsUtf8) {
+ // This uses the Unicode reference implementation to do the
+ // conversion from UTF-8 to wchar_t. The required files are
+ // ConvertUTF.h and ConvertUTF.c which should be included in
+ // the distribution but are publically available from unicode.org
+ // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
+ ConversionResult retval;
+ const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
+ if (sizeof(wchar_t) == sizeof(UTF32)) {
+ UTF32 * pUtf32 = (UTF32 *) a_pOutputData;
+ retval = ConvertUTF8toUTF32(
+ &pUtf8, pUtf8 + a_uInputDataLen,
+ &pUtf32, pUtf32 + a_uOutputDataSize,
+ lenientConversion);
+ }
+ else if (sizeof(wchar_t) == sizeof(UTF16)) {
+ UTF16 * pUtf16 = (UTF16 *) a_pOutputData;
+ retval = ConvertUTF8toUTF16(
+ &pUtf8, pUtf8 + a_uInputDataLen,
+ &pUtf16, pUtf16 + a_uOutputDataSize,
+ lenientConversion);
+ }
+ return retval == conversionOK;
+ }
+ else {
+ size_t retval = mbstowcs(a_pOutputData,
+ a_pInputData, a_uOutputDataSize);
+ return retval != (size_t)(-1);
+ }
+ }
+
+ /** Calculate the number of char required by the storage format of this
+ * data. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated string to calculate the number of
+ * bytes required to be converted to storage format.
+ * @return Number of bytes required by the string when
+ * converted to storage format. This size always
+ * includes space for the terminating NULL character.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeToStore(
+ const SI_CHAR * a_pInputData)
+ {
+ if (m_bStoreIsUtf8) {
+ // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
+ size_t uLen = 0;
+ while (a_pInputData[uLen]) {
+ ++uLen;
+ }
+ return (6 * uLen) + 1;
+ }
+ else {
+ size_t uLen = wcstombs(NULL, a_pInputData, 0);
+ if (uLen == (size_t)(-1)) {
+ return uLen;
+ }
+ return uLen + 1; // include NULL terminator
+ }
+ }
+
+ /** Convert the input string to the storage format of this data.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated source string to convert. All of
+ * the data will be converted including the
+ * terminating NULL character.
+ * @param a_pOutputData Pointer to the buffer to receive the converted
+ * string.
+ * @param a_uOutputDataSize Size of the output buffer in char.
+ * @return true if all of the input data, including the
+ * terminating NULL character was successfully
+ * converted.
+ */
+ bool ConvertToStore(
+ const SI_CHAR * a_pInputData,
+ char * a_pOutputData,
+ size_t a_uOutputDataSize
+ )
+ {
+ if (m_bStoreIsUtf8) {
+ // calc input string length (SI_CHAR type and size independent)
+ size_t uInputLen = 0;
+ while (a_pInputData[uInputLen]) {
+ ++uInputLen;
+ }
+ ++uInputLen; // include the NULL char
+
+ // This uses the Unicode reference implementation to do the
+ // conversion from wchar_t to UTF-8. The required files are
+ // ConvertUTF.h and ConvertUTF.c which should be included in
+ // the distribution but are publically available from unicode.org
+ // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
+ ConversionResult retval;
+ UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
+ if (sizeof(wchar_t) == sizeof(UTF32)) {
+ const UTF32 * pUtf32 = (const UTF32 *) a_pInputData;
+ retval = ConvertUTF32toUTF8(
+ &pUtf32, pUtf32 + uInputLen,
+ &pUtf8, pUtf8 + a_uOutputDataSize,
+ lenientConversion);
+ }
+ else if (sizeof(wchar_t) == sizeof(UTF16)) {
+ const UTF16 * pUtf16 = (const UTF16 *) a_pInputData;
+ retval = ConvertUTF16toUTF8(
+ &pUtf16, pUtf16 + uInputLen,
+ &pUtf8, pUtf8 + a_uOutputDataSize,
+ lenientConversion);
+ }
+ return retval == conversionOK;
+ }
+ else {
+ size_t retval = wcstombs(a_pOutputData,
+ a_pInputData, a_uOutputDataSize);
+ return retval != (size_t) -1;
+ }
+ }
+};
+
+#endif // SI_CONVERT_GENERIC
+
+
+// ---------------------------------------------------------------------------
+// SI_CONVERT_ICU
+// ---------------------------------------------------------------------------
+#ifdef SI_CONVERT_ICU
+
+#define SI_Case SI_GenericCase
+#define SI_NoCase SI_GenericNoCase
+
+#include <unicode/ucnv.h>
+
+/**
+ * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms.
+ */
+template<class SI_CHAR>
+class SI_ConvertW {
+ const char * m_pEncoding;
+ UConverter * m_pConverter;
+protected:
+ SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
+public:
+ SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
+ m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
+ }
+
+ /* copy and assignment */
+ SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
+ SI_ConvertW & operator=(const SI_ConvertW & rhs) {
+ m_pEncoding = rhs.m_pEncoding;
+ m_pConverter = NULL;
+ return *this;
+ }
+ ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); }
+
+ /** Calculate the number of UChar required for converting the input
+ * from the storage format. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to UChar.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @return Number of UChar required by the string when
+ * converted. If there are embedded NULL bytes in the
+ * input data, only the string up and not including
+ * the NULL byte will be converted.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen)
+ {
+ SI_ASSERT(a_uInputDataLen != (size_t) -1);
+
+ UErrorCode nError;
+
+ if (!m_pConverter) {
+ nError = U_ZERO_ERROR;
+ m_pConverter = ucnv_open(m_pEncoding, &nError);
+ if (U_FAILURE(nError)) {
+ return (size_t) -1;
+ }
+ }
+
+ nError = U_ZERO_ERROR;
+ ucnv_resetToUnicode(m_pConverter);
+ int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,
+ a_pInputData, (int32_t) a_uInputDataLen, &nError);
+ if (nError != U_BUFFER_OVERFLOW_ERROR) {
+ return (size_t) -1;
+ }
+
+ return (size_t) nLen;
+ }
+
+ /** Convert the input string from the storage format to UChar.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to UChar.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @param a_pOutputData Pointer to the output buffer to received the
+ * converted data.
+ * @param a_uOutputDataSize Size of the output buffer in UChar.
+ * @return true if all of the input data was successfully
+ * converted.
+ */
+ bool ConvertFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen,
+ UChar * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ UErrorCode nError;
+
+ if (!m_pConverter) {
+ nError = U_ZERO_ERROR;
+ m_pConverter = ucnv_open(m_pEncoding, &nError);
+ if (U_FAILURE(nError)) {
+ return false;
+ }
+ }
+
+ nError = U_ZERO_ERROR;
+ ucnv_resetToUnicode(m_pConverter);
+ ucnv_toUChars(m_pConverter,
+ a_pOutputData, (int32_t) a_uOutputDataSize,
+ a_pInputData, (int32_t) a_uInputDataLen, &nError);
+ if (U_FAILURE(nError)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /** Calculate the number of char required by the storage format of this
+ * data. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated string to calculate the number of
+ * bytes required to be converted to storage format.
+ * @return Number of bytes required by the string when
+ * converted to storage format. This size always
+ * includes space for the terminating NULL character.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeToStore(
+ const UChar * a_pInputData)
+ {
+ UErrorCode nError;
+
+ if (!m_pConverter) {
+ nError = U_ZERO_ERROR;
+ m_pConverter = ucnv_open(m_pEncoding, &nError);
+ if (U_FAILURE(nError)) {
+ return (size_t) -1;
+ }
+ }
+
+ nError = U_ZERO_ERROR;
+ ucnv_resetFromUnicode(m_pConverter);
+ int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,
+ a_pInputData, -1, &nError);
+ if (nError != U_BUFFER_OVERFLOW_ERROR) {
+ return (size_t) -1;
+ }
+
+ return (size_t) nLen + 1;
+ }
+
+ /** Convert the input string to the storage format of this data.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated source string to convert. All of
+ * the data will be converted including the
+ * terminating NULL character.
+ * @param a_pOutputData Pointer to the buffer to receive the converted
+ * string.
+ * @param a_pOutputDataSize Size of the output buffer in char.
+ * @return true if all of the input data, including the
+ * terminating NULL character was successfully
+ * converted.
+ */
+ bool ConvertToStore(
+ const UChar * a_pInputData,
+ char * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ UErrorCode nError;
+
+ if (!m_pConverter) {
+ nError = U_ZERO_ERROR;
+ m_pConverter = ucnv_open(m_pEncoding, &nError);
+ if (U_FAILURE(nError)) {
+ return false;
+ }
+ }
+
+ nError = U_ZERO_ERROR;
+ ucnv_resetFromUnicode(m_pConverter);
+ ucnv_fromUChars(m_pConverter,
+ a_pOutputData, (int32_t) a_uOutputDataSize,
+ a_pInputData, -1, &nError);
+ if (U_FAILURE(nError)) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+#endif // SI_CONVERT_ICU
+
+
+// ---------------------------------------------------------------------------
+// SI_CONVERT_WIN32
+// ---------------------------------------------------------------------------
+#ifdef SI_CONVERT_WIN32
+
+#define SI_Case SI_GenericCase
+
+// Windows CE doesn't have errno or MBCS libraries
+#ifdef _WIN32_WCE
+# ifndef SI_NO_MBCS
+# define SI_NO_MBCS
+# endif
+#endif
+
+#include <windows.h>
+#ifdef SI_NO_MBCS
+# define SI_NoCase SI_GenericNoCase
+#else // !SI_NO_MBCS
+/**
+ * Case-insensitive comparison class using Win32 MBCS functions. This class
+ * returns a case-insensitive semi-collation order for MBCS text. It may not
+ * be safe for UTF-8 text returned in char format as we don't know what
+ * characters will be folded by the function! Therefore, if you are using
+ * SI_CHAR == char and SetUnicode(true), then you need to use the generic
+ * SI_NoCase class instead.
+ */
+#include <mbstring.h>
+template<class SI_CHAR>
+struct SI_NoCase {
+ bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
+ if (sizeof(SI_CHAR) == sizeof(char)) {
+ return _mbsicmp((const unsigned char *)pLeft,
+ (const unsigned char *)pRight) < 0;
+ }
+ if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
+ return _wcsicmp((const wchar_t *)pLeft,
+ (const wchar_t *)pRight) < 0;
+ }
+ return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight);
+ }
+};
+#endif // SI_NO_MBCS
+
+/**
+ * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses
+ * only the Win32 functions and doesn't require the external Unicode UTF-8
+ * conversion library. It will not work on Windows 95 without using Microsoft
+ * Layer for Unicode in your application.
+ */
+template<class SI_CHAR>
+class SI_ConvertW {
+ UINT m_uCodePage;
+protected:
+ SI_ConvertW() { }
+public:
+ SI_ConvertW(bool a_bStoreIsUtf8) {
+ m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP;
+ }
+
+ /* copy and assignment */
+ SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
+ SI_ConvertW & operator=(const SI_ConvertW & rhs) {
+ m_uCodePage = rhs.m_uCodePage;
+ return *this;
+ }
+
+ /** Calculate the number of SI_CHAR required for converting the input
+ * from the storage format. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @return Number of SI_CHAR required by the string when
+ * converted. If there are embedded NULL bytes in the
+ * input data, only the string up and not including
+ * the NULL byte will be converted.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen)
+ {
+ SI_ASSERT(a_uInputDataLen != (size_t) -1);
+
+ int retval = MultiByteToWideChar(
+ m_uCodePage, 0,
+ a_pInputData, (int) a_uInputDataLen,
+ 0, 0);
+ return (size_t)(retval > 0 ? retval : -1);
+ }
+
+ /** Convert the input string from the storage format to SI_CHAR.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @param a_pOutputData Pointer to the output buffer to received the
+ * converted data.
+ * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
+ * @return true if all of the input data was successfully
+ * converted.
+ */
+ bool ConvertFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen,
+ SI_CHAR * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ int nSize = MultiByteToWideChar(
+ m_uCodePage, 0,
+ a_pInputData, (int) a_uInputDataLen,
+ (wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
+ return (nSize > 0);
+ }
+
+ /** Calculate the number of char required by the storage format of this
+ * data. The storage format is always UTF-8.
+ *
+ * @param a_pInputData NULL terminated string to calculate the number of
+ * bytes required to be converted to storage format.
+ * @return Number of bytes required by the string when
+ * converted to storage format. This size always
+ * includes space for the terminating NULL character.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeToStore(
+ const SI_CHAR * a_pInputData)
+ {
+ int retval = WideCharToMultiByte(
+ m_uCodePage, 0,
+ (const wchar_t *) a_pInputData, -1,
+ 0, 0, 0, 0);
+ return (size_t) (retval > 0 ? retval : -1);
+ }
+
+ /** Convert the input string to the storage format of this data.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated source string to convert. All of
+ * the data will be converted including the
+ * terminating NULL character.
+ * @param a_pOutputData Pointer to the buffer to receive the converted
+ * string.
+ * @param a_pOutputDataSize Size of the output buffer in char.
+ * @return true if all of the input data, including the
+ * terminating NULL character was successfully
+ * converted.
+ */
+ bool ConvertToStore(
+ const SI_CHAR * a_pInputData,
+ char * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ int retval = WideCharToMultiByte(
+ m_uCodePage, 0,
+ (const wchar_t *) a_pInputData, -1,
+ a_pOutputData, (int) a_uOutputDataSize, 0, 0);
+ return retval > 0;
+ }
+};
+
+#endif // SI_CONVERT_WIN32
+
+
+// ---------------------------------------------------------------------------
+// TYPE DEFINITIONS
+// ---------------------------------------------------------------------------
+
+typedef CSimpleIniTempl<char,
+ SI_NoCase<char>,SI_ConvertA<char> > CSimpleIniA;
+typedef CSimpleIniTempl<char,
+ SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA;
+
+#if defined(SI_CONVERT_ICU)
+typedef CSimpleIniTempl<UChar,
+ SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;
+typedef CSimpleIniTempl<UChar,
+ SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;
+#else
+typedef CSimpleIniTempl<wchar_t,
+ SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;
+typedef CSimpleIniTempl<wchar_t,
+ SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;
+#endif
+
+#ifdef _UNICODE
+# define CSimpleIni CSimpleIniW
+# define CSimpleIniCase CSimpleIniCaseW
+# define SI_NEWLINE SI_NEWLINE_W
+#else // !_UNICODE
+# define CSimpleIni CSimpleIniA
+# define CSimpleIniCase CSimpleIniCaseA
+# define SI_NEWLINE SI_NEWLINE_A
+#endif // _UNICODE
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif // INCLUDED_SimpleIni_h
+
|