Path: blob/master/dep/simpleini/include/SimpleIni.h
4255 views
/** @mainpage12<table>3<tr><th>Library <td>SimpleIni4<tr><th>File <td>SimpleIni.h5<tr><th>Author <td>Brodie Thiesfield6<tr><th>Source <td>https://github.com/brofield/simpleini7<tr><th>Version <td>4.228</table>910Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation.1112@section intro INTRODUCTION1314This component allows an INI-style configuration file to be used on both15Windows and Linux/Unix. It is fast, simple and source code using this16component will compile unchanged on either OS.1718@section features FEATURES1920- MIT Licence allows free use in all software (including GPL and commercial)21- multi-platform (Windows CE/9x/NT..10/etc, Linux, MacOSX, Unix)22- loading and saving of INI-style configuration files23- configuration files can have any newline format on all platforms24- liberal acceptance of file format25- key/values with no section26- removal of whitespace around sections, keys and values27- support for multi-line values (values with embedded newline characters)28- optional support for multiple keys with the same name29- optional case-insensitive sections and keys (for ASCII characters only)30- saves files with sections and keys in the same order as they were loaded31- preserves comments on the file, section and keys where possible.32- supports both char or wchar_t programming interfaces33- supports both MBCS (system locale) and UTF-8 file encodings34- system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file35- support for non-ASCII characters in section, keys, values and comments36- support for non-standard character types or file encodings37via user-written converter classes38- support for adding/modifying values programmatically39- should compile cleanly without warning usually at the strictest warning level40- it has been tested with the following compilers:41- Windows/VC6 (warning level 3)42- Windows/VC.NET 2003 (warning level 4)43- Windows/VC 2005 (warning level 4)44- Windows/VC 2019 (warning level 4)45- Linux/gcc (-Wall)46- Mac OS/c++ (-Wall)4748@section usage USAGE SUMMARY4950-# Decide if you will be using utf8 or MBCS files, and working with the51data in utf8, wchar_t or ICU chars.52-# If you will only be using straight utf8 files and access the data via the53char interface, then you do not need any conversion library and could define54SI_NO_CONVERSION. Note that no conversion also means no validation of the data.55If no converter is specified then the default converter is SI_CONVERT_GENERIC56on Mac/Linux and SI_CONVERT_WIN32 on Windows. If you need widechar support on57Mac/Linux then use either SI_CONVERT_GENERIC or SI_CONVERT_ICU. These are also58supported on all platforms.59-# Define the appropriate symbol for the converter you wish to use and60include the SimpleIni.h header file.61-# Declare an instance of the appropriate class. Note that the following62definitions are just shortcuts for commonly used types. Other types63(PRUnichar, unsigned short, unsigned char) are also possible.64<table>65<tr><th>Interface <th>Case-sensitive <th>Load UTF-8 <th>Load MBCS <th>Typedef66<tr><th>SI_NO_CONVERSION67<tr><td>char <td>No <td>Yes <td>No <td>CSimpleIniA68<tr><td>char <td>Yes <td>Yes <td>No <td>CSimpleIniCaseA69<tr><th>SI_CONVERT_GENERIC70<tr><td>char <td>No <td>Yes <td>Yes #1 <td>CSimpleIniA71<tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA72<tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW73<tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW74<tr><th>SI_CONVERT_WIN3275<tr><td>char <td>No <td>No #2 <td>Yes <td>CSimpleIniA76<tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA77<tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW78<tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW79<tr><th>SI_CONVERT_ICU80<tr><td>char <td>No <td>Yes <td>Yes <td>CSimpleIniA81<tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA82<tr><td>UChar <td>No <td>Yes <td>Yes <td>CSimpleIniW83<tr><td>UChar <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW84</table>85#1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.<br>86#2 Only affects Windows. On Windows this uses MBCS functions and87so may fold case incorrectly leading to uncertain results.88-# Set all the options that you require, see all the Set*() options below.89The SetUnicode() option is very common and can be specified in the constructor.90-# Call LoadData() or LoadFile() to load and parse the INI configuration file91-# Access and modify the data of the file using the following functions92<table>93<tr><td>GetAllSections <td>Return all section names94<tr><td>GetAllKeys <td>Return all key names within a section95<tr><td>GetAllValues <td>Return all values within a section & key96<tr><td>GetSection <td>Return all key names and values in a section97<tr><td>GetSectionSize <td>Return the number of keys in a section98<tr><td>GetValue <td>Return a value for a section & key99<tr><td>SetValue <td>Add or update a value for a section & key100<tr><td>Delete <td>Remove a section, or a key from a section101<tr><td>SectionExists <td>Does a section exist?102<tr><td>KeyExists <td>Does a key exist?103</table>104-# Call Save() or SaveFile() to save the INI configuration data105106@section iostreams IO STREAMS107108SimpleIni supports reading from and writing to STL IO streams. Enable this109by defining SI_SUPPORT_IOSTREAMS before including the SimpleIni.h header110file. Ensure that if the streams are backed by a file (e.g. ifstream or111ofstream) then the flag ios_base::binary has been used when the file was112opened.113114@section multiline MULTI-LINE VALUES115116Values that span multiple lines are created using the following format.117118<pre>119key = <<<ENDTAG120.... multiline value ....121ENDTAG122</pre>123124Note the following:125- The text used for ENDTAG can be anything and is used to find126where the multi-line text ends.127- The newline after ENDTAG in the start tag, and the newline128before ENDTAG in the end tag is not included in the data value.129- The ending tag must be on it's own line with no whitespace before130or after it.131- The multi-line value is modified at load so that each line in the value132is delimited by a single '\\n' character on all platforms. At save time133it will be converted into the newline format used by the current134platform.135136@section comments COMMENTS137138Comments are preserved in the file within the following restrictions:139- Every file may have a single "file comment". It must start with the140first character in the file, and will end with the first non-comment141line in the file.142- Every section may have a single "section comment". It will start143with the first comment line following the file comment, or the last144data entry. It ends at the beginning of the section.145- Every key may have a single "key comment". This comment will start146with the first comment line following the section start, or the file147comment if there is no section name.148- Comments are set at the time that the file, section or key is first149created. The only way to modify a comment on a section or a key is to150delete that entry and recreate it with the new comment. There is no151way to change the file comment.152153@section save SAVE ORDER154155The sections and keys are written out in the same order as they were156read in from the file. Sections and keys added to the data after the157file has been loaded will be added to the end of the file when it is158written. There is no way to specify the location of a section or key159other than in first-created, first-saved order.160161@section notes NOTES162163- To load UTF-8 data on Windows 95, you need to use Microsoft Layer for164Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU.165- When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked.166- When using SI_CONVERT_ICU, ICU header files must be on the include167path and icuuc.lib must be linked in.168- To load a UTF-8 file on Windows AND expose it with SI_CHAR == char,169you should use SI_CONVERT_GENERIC.170- The collation (sorting) order used for sections and keys returned from171iterators is NOT DEFINED. If collation order of the text is important172then it should be done yourself by either supplying a replacement173SI_STRLESS class, or by sorting the strings external to this library.174- Usage of the <mbstring.h> header on Windows can be disabled by defining175SI_NO_MBCS. This is defined automatically on Windows CE platforms.176- Not thread-safe so manage your own locking177178@section contrib CONTRIBUTIONS179180Many thanks to the following contributors:181182- 2010/05/03: Tobias Gehrig: added GetDoubleValue()183- See list of many contributors in github184185@section licence MIT LICENCE186187The licence text below is the boilerplate "MIT Licence" used from:188http://www.opensource.org/licenses/mit-license.php189190Copyright (c) 2006-2024, Brodie Thiesfield191192Permission is hereby granted, free of charge, to any person obtaining a copy193of this software and associated documentation files (the "Software"), to deal194in the Software without restriction, including without limitation the rights195to use, copy, modify, merge, publish, distribute, sublicense, and/or sell196copies of the Software, and to permit persons to whom the Software is furnished197to do so, subject to the following conditions:198199The above copyright notice and this permission notice shall be included in200all copies or substantial portions of the Software.201202THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR203IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS204FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR205COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER206IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN207CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.208*/209210#ifndef INCLUDED_SimpleIni_h211#define INCLUDED_SimpleIni_h212213#if defined(_MSC_VER) && (_MSC_VER >= 1020)214# pragma once215#endif216217// Disable these warnings in MSVC:218// 4127 "conditional expression is constant" as the conversion classes trigger219// it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will220// be optimized away in a release build.221// 4503 'insert' : decorated name length exceeded, name was truncated222// 4702 "unreachable code" as the MS STL header causes it in release mode.223// Again, the code causing the warning will be cleaned up by the compiler.224// 4786 "identifier truncated to 256 characters" as this is thrown hundreds225// of times VC6 as soon as STL is used.226#ifdef _MSC_VER227# pragma warning (push)228# pragma warning (disable: 4127 4503 4702 4786)229#endif230231#include <cstring>232#include <cstdlib>233#include <string>234#include <map>235#include <list>236#include <algorithm>237#include <stdio.h>238239#ifdef SI_SUPPORT_IOSTREAMS240# include <iostream>241#endif // SI_SUPPORT_IOSTREAMS242243#ifdef _DEBUG244# ifndef assert245# include <cassert>246# endif247# define SI_ASSERT(x) assert(x)248#else249# define SI_ASSERT(x)250#endif251252using SI_Error = int;253254constexpr int SI_OK = 0; //!< No error255constexpr int SI_UPDATED = 1; //!< An existing value was updated256constexpr int SI_INSERTED = 2; //!< A new value was inserted257258// note: test for any error with (retval < 0)259constexpr int SI_FAIL = -1; //!< Generic failure260constexpr int SI_NOMEM = -2; //!< Out of memory error261constexpr int SI_FILE = -3; //!< File error (see errno for detail error)262263#define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"264265#ifdef _WIN32266# define SI_NEWLINE_A "\r\n"267# define SI_NEWLINE_W L"\r\n"268#else // !_WIN32269# define SI_NEWLINE_A "\n"270# define SI_NEWLINE_W L"\n"271#endif // _WIN32272273#if defined(SI_CONVERT_ICU)274# include <unicode/ustring.h>275#endif276277#if defined(_WIN32)278# define SI_HAS_WIDE_FILE279# define SI_WCHAR_T wchar_t280#elif defined(SI_CONVERT_ICU)281# define SI_HAS_WIDE_FILE282# define SI_WCHAR_T UChar283#endif284285286// ---------------------------------------------------------------------------287// MAIN TEMPLATE CLASS288// ---------------------------------------------------------------------------289290/** Simple INI file reader.291292This can be instantiated with the choice of unicode or native characterset,293and case sensitive or insensitive comparisons of section and key names.294The supported combinations are pre-defined with the following typedefs:295296<table>297<tr><th>Interface <th>Case-sensitive <th>Typedef298<tr><td>char <td>No <td>CSimpleIniA299<tr><td>char <td>Yes <td>CSimpleIniCaseA300<tr><td>wchar_t <td>No <td>CSimpleIniW301<tr><td>wchar_t <td>Yes <td>CSimpleIniCaseW302</table>303304Note that using other types for the SI_CHAR is supported. For instance,305unsigned char, unsigned short, etc. Note that where the alternative type306is a different size to char/wchar_t you may need to supply new helper307classes for SI_STRLESS and SI_CONVERTER.308*/309template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>310class CSimpleIniTempl311{312public:313typedef SI_CHAR SI_CHAR_T;314315/** key entry */316struct Entry {317const SI_CHAR * pItem;318const SI_CHAR * pComment;319int nOrder;320321Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0)322: pItem(a_pszItem)323, pComment(NULL)324, nOrder(a_nOrder)325{ }326Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder)327: pItem(a_pszItem)328, pComment(a_pszComment)329, nOrder(a_nOrder)330{ }331Entry(const Entry & rhs) { operator=(rhs); }332Entry & operator=(const Entry & rhs) {333pItem = rhs.pItem;334pComment = rhs.pComment;335nOrder = rhs.nOrder;336return *this;337}338339#if defined(_MSC_VER) && _MSC_VER <= 1200340/** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */341bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); }342bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); }343#endif344345/** Strict less ordering by name of key only */346struct KeyOrder {347bool operator()(const Entry & lhs, const Entry & rhs) const {348const static SI_STRLESS isLess = SI_STRLESS();349return isLess(lhs.pItem, rhs.pItem);350}351};352353/** Strict less ordering by order, and then name of key */354struct LoadOrder {355bool operator()(const Entry & lhs, const Entry & rhs) const {356if (lhs.nOrder != rhs.nOrder) {357return lhs.nOrder < rhs.nOrder;358}359return KeyOrder()(lhs.pItem, rhs.pItem);360}361};362};363364/** map keys to values */365typedef std::multimap<Entry,const SI_CHAR *,typename Entry::KeyOrder> TKeyVal;366367/** map sections to key/value map */368typedef std::map<Entry,TKeyVal,typename Entry::KeyOrder> TSection;369370/** set of dependent string pointers. Note that these pointers are371dependent on memory owned by CSimpleIni.372*/373typedef std::list<Entry> TNamesDepend;374375/** interface definition for the OutputWriter object to pass to Save()376in order to output the INI file data.377*/378class OutputWriter {379public:380OutputWriter() { }381virtual ~OutputWriter() { }382virtual void Write(const char * a_pBuf) = 0;383private:384OutputWriter(const OutputWriter &); // disable385OutputWriter & operator=(const OutputWriter &); // disable386};387388/** OutputWriter class to write the INI data to a file */389class FileWriter : public OutputWriter {390FILE * m_file;391public:392FileWriter(FILE * a_file) : m_file(a_file) { }393void Write(const char * a_pBuf) {394fputs(a_pBuf, m_file);395}396private:397FileWriter(const FileWriter &); // disable398FileWriter & operator=(const FileWriter &); // disable399};400401/** OutputWriter class to write the INI data to a string */402class StringWriter : public OutputWriter {403std::string & m_string;404public:405StringWriter(std::string & a_string) : m_string(a_string) { }406void Write(const char * a_pBuf) {407m_string.append(a_pBuf);408}409private:410StringWriter(const StringWriter &); // disable411StringWriter & operator=(const StringWriter &); // disable412};413414#ifdef SI_SUPPORT_IOSTREAMS415/** OutputWriter class to write the INI data to an ostream */416class StreamWriter : public OutputWriter {417std::ostream & m_ostream;418public:419StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { }420void Write(const char * a_pBuf) {421m_ostream << a_pBuf;422}423private:424StreamWriter(const StreamWriter &); // disable425StreamWriter & operator=(const StreamWriter &); // disable426};427#endif // SI_SUPPORT_IOSTREAMS428429/** Characterset conversion utility class to convert strings to the430same format as is used for the storage.431*/432class Converter : private SI_CONVERTER {433public:434Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) {435m_scratch.resize(1024);436}437Converter(const Converter & rhs) { operator=(rhs); }438Converter & operator=(const Converter & rhs) {439m_scratch = rhs.m_scratch;440return *this;441}442bool ConvertToStore(const SI_CHAR * a_pszString) {443size_t uLen = SI_CONVERTER::SizeToStore(a_pszString);444if (uLen == (size_t)(-1)) {445return false;446}447while (uLen > m_scratch.size()) {448m_scratch.resize(m_scratch.size() * 2);449}450return SI_CONVERTER::ConvertToStore(451a_pszString,452const_cast<char*>(m_scratch.data()),453m_scratch.size());454}455const char * Data() { return m_scratch.data(); }456private:457std::string m_scratch;458};459460public:461/*-----------------------------------------------------------------------*/462463/** Default constructor.464465@param a_bIsUtf8 See the method SetUnicode() for details.466@param a_bMultiKey See the method SetMultiKey() for details.467@param a_bMultiLine See the method SetMultiLine() for details.468*/469CSimpleIniTempl(470bool a_bIsUtf8 = false,471bool a_bMultiKey = false,472bool a_bMultiLine = false473);474475/** Destructor */476~CSimpleIniTempl();477478/** Deallocate all memory stored by this object */479void Reset();480481/** Has any data been loaded */482bool IsEmpty() const { return m_data.empty(); }483484/*-----------------------------------------------------------------------*/485/** @{ @name Settings */486487/** Set the storage format of the INI data. This affects both the loading488and saving of the INI data using all of the Load/Save API functions.489This value cannot be changed after any INI data has been loaded.490491If the file is not set to Unicode (UTF-8), then the data encoding is492assumed to be the OS native encoding. This encoding is the system493locale on Linux/Unix and the legacy MBCS encoding on Windows NT/2K/XP.494If the storage format is set to Unicode then the file will be loaded495as UTF-8 encoded data regardless of the native file encoding. If496SI_CHAR == char then all of the char* parameters take and return UTF-8497encoded data regardless of the system locale.498499\param a_bIsUtf8 Assume UTF-8 encoding for the source?500*/501void SetUnicode(bool a_bIsUtf8 = true) {502if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8;503}504505/** Get the storage format of the INI data. */506bool IsUnicode() const { return m_bStoreIsUtf8; }507508/** Should multiple identical keys be permitted in the file. If set to false509then the last value encountered will be used as the value of the key.510If set to true, then all values will be available to be queried. For511example, with the following input:512513<pre>514[section]515test=value1516test=value2517</pre>518519Then with SetMultiKey(true), both of the values "value1" and "value2"520will be returned for the key test. If SetMultiKey(false) is used, then521the value for "test" will only be "value2". This value may be changed522at any time.523524\param a_bAllowMultiKey Allow multi-keys in the source?525*/526void SetMultiKey(bool a_bAllowMultiKey = true) {527m_bAllowMultiKey = a_bAllowMultiKey;528}529530/** Get the storage format of the INI data. */531bool IsMultiKey() const { return m_bAllowMultiKey; }532533/** Should data values be permitted to span multiple lines in the file. If534set to false then the multi-line construct <<<TAG as a value will be535returned as is instead of loading the data. This value may be changed536at any time.537538\param a_bAllowMultiLine Allow multi-line values in the source?539*/540void SetMultiLine(bool a_bAllowMultiLine = true) {541m_bAllowMultiLine = a_bAllowMultiLine;542}543544/** Query the status of multi-line data */545bool IsMultiLine() const { return m_bAllowMultiLine; }546547/** Should spaces be added around the equals sign when writing key/value548pairs out. When true, the result will be "key = value". When false,549the result will be "key=value". This value may be changed at any time.550551\param a_bSpaces Add spaces around the equals sign?552*/553void SetSpaces(bool a_bSpaces = true) {554m_bSpaces = a_bSpaces;555}556557/** Query the status of spaces output */558bool UsingSpaces() const { return m_bSpaces; }559560561/** Should we recognise and parse quotes in single line values?562563\param a_bParseQuotes Parse quoted data in values?564*/565void SetQuotes(bool a_bParseQuotes = true) {566m_bParseQuotes = a_bParseQuotes;567}568569/** Are we permitting keys and values to be quoted? */570bool UsingQuotes() const { return m_bParseQuotes; }571572/** When reading/writing an ini file, do we require every key to have an equals573sign to delineate a valid key value. If false, then every valid key must574have an equals sign and any lines without an equals sign is ignored. If575true then keys do not require an equals sign to be considered a key. Note576that this means that any non-commented line of text would become a key.577578\param a_bAllowKeyOnly Permit keys without an equals sign or value.579*/580void SetAllowKeyOnly(bool a_bAllowKeyOnly = true) {581m_bAllowKeyOnly = a_bAllowKeyOnly;582}583584/** Do we allow keys to exist without a value or equals sign? */585bool GetAllowKeyOnly() const { return m_bAllowKeyOnly; }586587588589/*-----------------------------------------------------------------------*/590/** @}591@{ @name Loading INI Data */592593/** Load an INI file from disk into memory594595@param a_pszFile Path of the file to be loaded. This will be passed596to fopen() and so must be a valid path for the597current platform.598599@return SI_Error See error definitions600*/601SI_Error LoadFile(602const char * a_pszFile603);604605#ifdef SI_HAS_WIDE_FILE606/** Load an INI file from disk into memory607608@param a_pwszFile Path of the file to be loaded in UTF-16.609610@return SI_Error See error definitions611*/612SI_Error LoadFile(613const SI_WCHAR_T * a_pwszFile614);615#endif // SI_HAS_WIDE_FILE616617/** Load the file from a file pointer.618619@param a_fpFile Valid file pointer to read the file data from. The620file will be read until end of file.621622@return SI_Error See error definitions623*/624SI_Error LoadFile(625FILE * a_fpFile626);627628#ifdef SI_SUPPORT_IOSTREAMS629/** Load INI file data from an istream.630631@param a_istream Stream to read from632633@return SI_Error See error definitions634*/635SI_Error LoadData(636std::istream & a_istream637);638#endif // SI_SUPPORT_IOSTREAMS639640/** Load INI file data direct from a std::string641642@param a_strData Data to be loaded643644@return SI_Error See error definitions645*/646SI_Error LoadData(const std::string & a_strData) {647return LoadData(a_strData.c_str(), a_strData.size());648}649650/** Load INI file data direct from memory651652@param a_pData Data to be loaded653@param a_uDataLen Length of the data in bytes654655@return SI_Error See error definitions656*/657SI_Error LoadData(658const char * a_pData,659size_t a_uDataLen660);661662/*-----------------------------------------------------------------------*/663/** @}664@{ @name Saving INI Data */665666/** Save an INI file from memory to disk667668@param a_pszFile Path of the file to be saved. This will be passed669to fopen() and so must be a valid path for the670current platform.671672@param a_bAddSignature Prepend the UTF-8 BOM if the output data is673in UTF-8 format. If it is not UTF-8 then674this parameter is ignored.675676@return SI_Error See error definitions677*/678SI_Error SaveFile(679const char * a_pszFile,680bool a_bAddSignature = true681) const;682683#ifdef SI_HAS_WIDE_FILE684/** Save an INI file from memory to disk685686@param a_pwszFile Path of the file to be saved in UTF-16.687688@param a_bAddSignature Prepend the UTF-8 BOM if the output data is689in UTF-8 format. If it is not UTF-8 then690this parameter is ignored.691692@return SI_Error See error definitions693*/694SI_Error SaveFile(695const SI_WCHAR_T * a_pwszFile,696bool a_bAddSignature = true697) const;698#endif // _WIN32699700/** Save the INI data to a file. See Save() for details.701702@param a_pFile Handle to a file. File should be opened for703binary output.704705@param a_bAddSignature Prepend the UTF-8 BOM if the output data is in706UTF-8 format. If it is not UTF-8 then this value is707ignored. Do not set this to true if anything has708already been written to the file.709710@return SI_Error See error definitions711*/712SI_Error SaveFile(713FILE * a_pFile,714bool a_bAddSignature = false715) const;716717/** Save the INI data. The data will be written to the output device718in a format appropriate to the current data, selected by:719720<table>721<tr><th>SI_CHAR <th>FORMAT722<tr><td>char <td>same format as when loaded (MBCS or UTF-8)723<tr><td>wchar_t <td>UTF-8724<tr><td>other <td>UTF-8725</table>726727Note that comments from the original data is preserved as per the728documentation on comments. The order of the sections and values729from the original file will be preserved.730731Any data prepended or appended to the output device must use the the732same format (MBCS or UTF-8). You may use the GetConverter() method to733convert text to the correct format regardless of the output format734being used by SimpleIni.735736To add a BOM to UTF-8 data, write it out manually at the very beginning737like is done in SaveFile when a_bUseBOM is true.738739@param a_oOutput Output writer to write the data to.740741@param a_bAddSignature Prepend the UTF-8 BOM if the output data is in742UTF-8 format. If it is not UTF-8 then this value is743ignored. Do not set this to true if anything has744already been written to the OutputWriter.745746@return SI_Error See error definitions747*/748SI_Error Save(749OutputWriter & a_oOutput,750bool a_bAddSignature = false751) const;752753#ifdef SI_SUPPORT_IOSTREAMS754/** Save the INI data to an ostream. See Save() for details.755756@param a_ostream String to have the INI data appended to.757758@param a_bAddSignature Prepend the UTF-8 BOM if the output data is in759UTF-8 format. If it is not UTF-8 then this value is760ignored. Do not set this to true if anything has761already been written to the stream.762763@return SI_Error See error definitions764*/765SI_Error Save(766std::ostream & a_ostream,767bool a_bAddSignature = false768) const769{770StreamWriter writer(a_ostream);771return Save(writer, a_bAddSignature);772}773#endif // SI_SUPPORT_IOSTREAMS774775/** Append the INI data to a string. See Save() for details.776777@param a_sBuffer String to have the INI data appended to.778779@param a_bAddSignature Prepend the UTF-8 BOM if the output data is in780UTF-8 format. If it is not UTF-8 then this value is781ignored. Do not set this to true if anything has782already been written to the string.783784@return SI_Error See error definitions785*/786SI_Error Save(787std::string & a_sBuffer,788bool a_bAddSignature = false789) const790{791StringWriter writer(a_sBuffer);792return Save(writer, a_bAddSignature);793}794795/*-----------------------------------------------------------------------*/796/** @}797@{ @name Accessing INI Data */798799/** Retrieve the number keys across all sections.800@return number of keys currently present.801*/802size_t GetKeyCount() const;803804/** Retrieve all section names. The list is returned as an STL vector of805names and can be iterated or searched as necessary. Note that the806sort order of the returned strings is NOT DEFINED. You can sort807the names into the load order if desired. Search this file for ".sort"808for an example.809810NOTE! This structure contains only pointers to strings. The actual811string data is stored in memory owned by CSimpleIni. Ensure that the812CSimpleIni object is not destroyed or Reset() while these pointers813are in use!814815@param a_names Vector that will receive all of the section816names. See note above!817*/818void GetAllSections(819TNamesDepend & a_names820) const;821822/** Retrieve all unique key names in a section. The sort order of the823returned strings is NOT DEFINED. You can sort the names into the load824order if desired. Search this file for ".sort" for an example. Only825unique key names are returned.826827NOTE! This structure contains only pointers to strings. The actual828string data is stored in memory owned by CSimpleIni. Ensure that the829CSimpleIni object is not destroyed or Reset() while these strings830are in use!831832@param a_pSection Section to request data for833@param a_names List that will receive all of the key834names. See note above!835836@return true Section was found.837@return false Matching section was not found.838*/839bool GetAllKeys(840const SI_CHAR * a_pSection,841TNamesDepend & a_names842) const;843844/** Retrieve all values for a specific key. This method can be used when845multiple keys are both enabled and disabled. Note that the sort order846of the returned strings is NOT DEFINED. You can sort the names into847the load order if desired. Search this file for ".sort" for an example.848849NOTE! The returned values are pointers to string data stored in memory850owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed851or Reset while you are using this pointer!852853@param a_pSection Section to search854@param a_pKey Key to search for855@param a_values List to return if the key is not found856857@return true Key was found.858@return false Matching section/key was not found.859*/860bool GetAllValues(861const SI_CHAR * a_pSection,862const SI_CHAR * a_pKey,863TNamesDepend & a_values864) const;865866/** Query the number of keys in a specific section. Note that if multiple867keys are enabled, then this value may be different to the number of868keys returned by GetAllKeys.869870@param a_pSection Section to request data for871872@return -1 Section does not exist in the file873@return >=0 Number of keys in the section874*/875int GetSectionSize(876const SI_CHAR * a_pSection877) const;878879/** Retrieve all key and value pairs for a section. The data is returned880as a pointer to an STL map and can be iterated or searched as881desired. Note that multiple entries for the same key may exist when882multiple keys have been enabled.883884NOTE! This structure contains only pointers to strings. The actual885string data is stored in memory owned by CSimpleIni. Ensure that the886CSimpleIni object is not destroyed or Reset() while these strings887are in use!888889@param a_pSection Name of the section to return890@return Section data891*/892const TKeyVal * GetSection(893const SI_CHAR * a_pSection894) const;895896/** Test if a section exists. Convenience function */897inline bool SectionExists(898const SI_CHAR * a_pSection899) const {900return GetSection(a_pSection) != NULL;901}902903/** Test if the key exists in a section. Convenience function. */904inline bool KeyExists(905const SI_CHAR * a_pSection,906const SI_CHAR * a_pKey907) const {908return GetValue(a_pSection, a_pKey) != NULL;909}910911/** Retrieve the value for a specific key. If multiple keys are enabled912(see SetMultiKey) then only the first value associated with that key913will be returned, see GetAllValues for getting all values with multikey.914915NOTE! The returned value is a pointer to string data stored in memory916owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed917or Reset while you are using this pointer!918919@param a_pSection Section to search920@param a_pKey Key to search for921@param a_pDefault Value to return if the key is not found922@param a_pHasMultiple Optionally receive notification of if there are923multiple entries for this key.924925@return a_pDefault Key was not found in the section926@return other Value of the key927*/928const SI_CHAR * GetValue(929const SI_CHAR * a_pSection,930const SI_CHAR * a_pKey,931const SI_CHAR * a_pDefault = NULL,932bool * a_pHasMultiple = NULL933) const;934935/** Retrieve a numeric value for a specific key. If multiple keys are enabled936(see SetMultiKey) then only the first value associated with that key937will be returned, see GetAllValues for getting all values with multikey.938939@param a_pSection Section to search940@param a_pKey Key to search for941@param a_nDefault Value to return if the key is not found942@param a_pHasMultiple Optionally receive notification of if there are943multiple entries for this key.944945@return a_nDefault Key was not found in the section946@return other Value of the key947*/948long GetLongValue(949const SI_CHAR * a_pSection,950const SI_CHAR * a_pKey,951long a_nDefault = 0,952bool * a_pHasMultiple = NULL953) const;954955/** Retrieve a numeric value for a specific key. If multiple keys are enabled956(see SetMultiKey) then only the first value associated with that key957will be returned, see GetAllValues for getting all values with multikey.958959@param a_pSection Section to search960@param a_pKey Key to search for961@param a_nDefault Value to return if the key is not found962@param a_pHasMultiple Optionally receive notification of if there are963multiple entries for this key.964965@return a_nDefault Key was not found in the section966@return other Value of the key967*/968double GetDoubleValue(969const SI_CHAR * a_pSection,970const SI_CHAR * a_pKey,971double a_nDefault = 0,972bool * a_pHasMultiple = NULL973) const;974975/** Retrieve a boolean value for a specific key. If multiple keys are enabled976(see SetMultiKey) then only the first value associated with that key977will be returned, see GetAllValues for getting all values with multikey.978979Strings starting with "t", "y", "on" or "1" are returned as logically true.980Strings starting with "f", "n", "of" or "0" are returned as logically false.981For all other values the default is returned. Character comparisons are982case-insensitive.983984@param a_pSection Section to search985@param a_pKey Key to search for986@param a_bDefault Value to return if the key is not found987@param a_pHasMultiple Optionally receive notification of if there are988multiple entries for this key.989990@return a_nDefault Key was not found in the section991@return other Value of the key992*/993bool GetBoolValue(994const SI_CHAR * a_pSection,995const SI_CHAR * a_pKey,996bool a_bDefault = false,997bool * a_pHasMultiple = NULL998) const;9991000/** Add or update a section or value. This will always insert1001when multiple keys are enabled.10021003@param a_pSection Section to add or update1004@param a_pKey Key to add or update. Set to NULL to1005create an empty section.1006@param a_pValue Value to set. Set to NULL to create an1007empty section.1008@param a_pComment Comment to be associated with the section or the1009key. If a_pKey is NULL then it will be associated1010with the section, otherwise the key. Note that a1011comment may be set ONLY when the section or key is1012first created (i.e. when this function returns the1013value SI_INSERTED). If you wish to create a section1014with a comment then you need to create the section1015separately to the key. The comment string must be1016in full comment form already (have a comment1017character starting every line).1018@param a_bForceReplace Should all existing values in a multi-key INI1019file be replaced with this entry. This option has1020no effect if not using multi-key files. The1021difference between Delete/SetValue and SetValue1022with a_bForceReplace = true, is that the load1023order and comment will be preserved this way.10241025@return SI_Error See error definitions1026@return SI_UPDATED Value was updated1027@return SI_INSERTED Value was inserted1028*/1029SI_Error SetValue(1030const SI_CHAR * a_pSection,1031const SI_CHAR * a_pKey,1032const SI_CHAR * a_pValue,1033const SI_CHAR * a_pComment = NULL,1034bool a_bForceReplace = false1035)1036{1037return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true);1038}10391040/** Add or update a numeric value. This will always insert1041when multiple keys are enabled.10421043@param a_pSection Section to add or update1044@param a_pKey Key to add or update.1045@param a_nValue Value to set.1046@param a_pComment Comment to be associated with the key. See the1047notes on SetValue() for comments.1048@param a_bUseHex By default the value will be written to the file1049in decimal format. Set this to true to write it1050as hexadecimal.1051@param a_bForceReplace Should all existing values in a multi-key INI1052file be replaced with this entry. This option has1053no effect if not using multi-key files. The1054difference between Delete/SetLongValue and1055SetLongValue with a_bForceReplace = true, is that1056the load order and comment will be preserved this1057way.10581059@return SI_Error See error definitions1060@return SI_UPDATED Value was updated1061@return SI_INSERTED Value was inserted1062*/1063SI_Error SetLongValue(1064const SI_CHAR * a_pSection,1065const SI_CHAR * a_pKey,1066long a_nValue,1067const SI_CHAR * a_pComment = NULL,1068bool a_bUseHex = false,1069bool a_bForceReplace = false1070);10711072/** Add or update a double value. This will always insert1073when multiple keys are enabled.10741075@param a_pSection Section to add or update1076@param a_pKey Key to add or update.1077@param a_nValue Value to set.1078@param a_pComment Comment to be associated with the key. See the1079notes on SetValue() for comments.1080@param a_bForceReplace Should all existing values in a multi-key INI1081file be replaced with this entry. This option has1082no effect if not using multi-key files. The1083difference between Delete/SetDoubleValue and1084SetDoubleValue with a_bForceReplace = true, is that1085the load order and comment will be preserved this1086way.10871088@return SI_Error See error definitions1089@return SI_UPDATED Value was updated1090@return SI_INSERTED Value was inserted1091*/1092SI_Error SetDoubleValue(1093const SI_CHAR * a_pSection,1094const SI_CHAR * a_pKey,1095double a_nValue,1096const SI_CHAR * a_pComment = NULL,1097bool a_bForceReplace = false1098);10991100/** Add or update a boolean value. This will always insert1101when multiple keys are enabled.11021103@param a_pSection Section to add or update1104@param a_pKey Key to add or update.1105@param a_bValue Value to set.1106@param a_pComment Comment to be associated with the key. See the1107notes on SetValue() for comments.1108@param a_bForceReplace Should all existing values in a multi-key INI1109file be replaced with this entry. This option has1110no effect if not using multi-key files. The1111difference between Delete/SetBoolValue and1112SetBoolValue with a_bForceReplace = true, is that1113the load order and comment will be preserved this1114way.11151116@return SI_Error See error definitions1117@return SI_UPDATED Value was updated1118@return SI_INSERTED Value was inserted1119*/1120SI_Error SetBoolValue(1121const SI_CHAR * a_pSection,1122const SI_CHAR * a_pKey,1123bool a_bValue,1124const SI_CHAR * a_pComment = NULL,1125bool a_bForceReplace = false1126);11271128/** Delete an entire section, or a key from a section. Note that the1129data returned by GetSection is invalid and must not be used after1130anything has been deleted from that section using this method.1131Note when multiple keys is enabled, this will delete all keys with1132that name; to selectively delete individual key/values, use1133DeleteValue.11341135@param a_pSection Section to delete key from, or if1136a_pKey is NULL, the section to remove.1137@param a_pKey Key to remove from the section. Set to1138NULL to remove the entire section.1139@param a_bRemoveEmpty If the section is empty after this key has1140been deleted, should the empty section be1141removed?11421143@return true Key or section was deleted.1144@return false Key or section was not found.1145*/1146bool Delete(1147const SI_CHAR * a_pSection,1148const SI_CHAR * a_pKey,1149bool a_bRemoveEmpty = false1150);11511152/** Delete an entire section, or a key from a section. If value is1153provided, only remove keys with the value. Note that the data1154returned by GetSection is invalid and must not be used after1155anything has been deleted from that section using this method.1156Note when multiple keys is enabled, all keys with the value will1157be deleted.11581159@param a_pSection Section to delete key from, or if1160a_pKey is NULL, the section to remove.1161@param a_pKey Key to remove from the section. Set to1162NULL to remove the entire section.1163@param a_pValue Value of key to remove from the section.1164Set to NULL to remove all keys.1165@param a_bRemoveEmpty If the section is empty after this key has1166been deleted, should the empty section be1167removed?11681169@return true Key/value or section was deleted.1170@return false Key/value or section was not found.1171*/1172bool DeleteValue(1173const SI_CHAR * a_pSection,1174const SI_CHAR * a_pKey,1175const SI_CHAR * a_pValue,1176bool a_bRemoveEmpty = false1177);11781179/*-----------------------------------------------------------------------*/1180/** @}1181@{ @name Converter */11821183/** Return a conversion object to convert text to the same encoding1184as is used by the Save(), SaveFile() and SaveString() functions.1185Use this to prepare the strings that you wish to append or prepend1186to the output INI data.1187*/1188Converter GetConverter() const {1189return Converter(m_bStoreIsUtf8);1190}11911192/*-----------------------------------------------------------------------*/1193/** @} */11941195private:1196// copying is not permitted1197CSimpleIniTempl(const CSimpleIniTempl &); // disabled1198CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled11991200/** Parse the data looking for a file comment and store it if found.1201*/1202SI_Error FindFileComment(1203SI_CHAR *& a_pData,1204bool a_bCopyStrings1205);12061207/** Parse the data looking for the next valid entry. The memory pointed to1208by a_pData is modified by inserting NULL characters. The pointer is1209updated to the current location in the block of text.1210*/1211bool FindEntry(1212SI_CHAR *& a_pData,1213const SI_CHAR *& a_pSection,1214const SI_CHAR *& a_pKey,1215const SI_CHAR *& a_pVal,1216const SI_CHAR *& a_pComment1217) const;12181219/** Add the section/key/value to our data.12201221@param a_pSection Section name. Sections will be created if they1222don't already exist.1223@param a_pKey Key name. May be NULL to create an empty section.1224Existing entries will be updated. New entries will1225be created.1226@param a_pValue Value for the key.1227@param a_pComment Comment to be associated with the section or the1228key. If a_pKey is NULL then it will be associated1229with the section, otherwise the key. This must be1230a string in full comment form already (have a1231comment character starting every line).1232@param a_bForceReplace Should all existing values in a multi-key INI1233file be replaced with this entry. This option has1234no effect if not using multi-key files. The1235difference between Delete/AddEntry and AddEntry1236with a_bForceReplace = true, is that the load1237order and comment will be preserved this way.1238@param a_bCopyStrings Should copies of the strings be made or not.1239If false then the pointers will be used as is.1240*/1241SI_Error AddEntry(1242const SI_CHAR * a_pSection,1243const SI_CHAR * a_pKey,1244const SI_CHAR * a_pValue,1245const SI_CHAR * a_pComment,1246bool a_bForceReplace,1247bool a_bCopyStrings1248);12491250/** Is the supplied character a whitespace character? */1251inline bool IsSpace(SI_CHAR ch) const {1252return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');1253}12541255/** Does the supplied character start a comment line? */1256inline bool IsComment(SI_CHAR ch) const {1257return (ch == ';' || ch == '#');1258}125912601261/** Skip over a newline character (or characters) for either DOS or UNIX */1262inline void SkipNewLine(SI_CHAR *& a_pData) const {1263a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1;1264}12651266/** Make a copy of the supplied string, replacing the original pointer */1267SI_Error CopyString(const SI_CHAR *& a_pString);12681269/** Delete a string from the copied strings buffer if necessary */1270void DeleteString(const SI_CHAR * a_pString);12711272/** Internal use of our string comparison function */1273bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const {1274const static SI_STRLESS isLess = SI_STRLESS();1275return isLess(a_pLeft, a_pRight);1276}12771278bool IsMultiLineTag(const SI_CHAR * a_pData) const;1279bool IsMultiLineData(const SI_CHAR * a_pData) const;1280bool IsSingleLineQuotedValue(const SI_CHAR* a_pData) const;1281bool LoadMultiLineText(1282SI_CHAR *& a_pData,1283const SI_CHAR *& a_pVal,1284const SI_CHAR * a_pTagName,1285bool a_bAllowBlankLinesInComment = false1286) const;1287bool IsNewLineChar(SI_CHAR a_c) const;12881289bool OutputMultiLineText(1290OutputWriter & a_oOutput,1291Converter & a_oConverter,1292const SI_CHAR * a_pText1293) const;12941295private:1296/** Copy of the INI file data in our character format. This will be1297modified when parsed to have NULL characters added after all1298interesting string entries. All of the string pointers to sections,1299keys and values point into this block of memory.1300*/1301SI_CHAR * m_pData;13021303/** Length of the data that we have stored. Used when deleting strings1304to determine if the string is stored here or in the allocated string1305buffer.1306*/1307size_t m_uDataLen;13081309/** File comment for this data, if one exists. */1310const SI_CHAR * m_pFileComment;13111312/** constant empty string */1313const SI_CHAR m_cEmptyString;13141315/** Parsed INI data. Section -> (Key -> Value). */1316TSection m_data;13171318/** This vector stores allocated memory for copies of strings that have1319been supplied after the file load. It will be empty unless SetValue()1320has been called.1321*/1322TNamesDepend m_strings;13231324/** Is the format of our datafile UTF-8 or MBCS? */1325bool m_bStoreIsUtf8;13261327/** Are multiple values permitted for the same key? */1328bool m_bAllowMultiKey;13291330/** Are data values permitted to span multiple lines? */1331bool m_bAllowMultiLine;13321333/** Should spaces be written out surrounding the equals sign? */1334bool m_bSpaces;13351336/** Should quoted data in values be recognized and parsed? */1337bool m_bParseQuotes;13381339/** Do keys always need to have an equals sign when reading/writing? */1340bool m_bAllowKeyOnly;13411342/** Next order value, used to ensure sections and keys are output in the1343same order that they are loaded/added.1344*/1345int m_nOrder;1346};13471348// ---------------------------------------------------------------------------1349// IMPLEMENTATION1350// ---------------------------------------------------------------------------13511352template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1353CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl(1354bool a_bIsUtf8,1355bool a_bAllowMultiKey,1356bool a_bAllowMultiLine1357)1358: m_pData(0)1359, m_uDataLen(0)1360, m_pFileComment(NULL)1361, m_cEmptyString(0)1362, m_bStoreIsUtf8(a_bIsUtf8)1363, m_bAllowMultiKey(a_bAllowMultiKey)1364, m_bAllowMultiLine(a_bAllowMultiLine)1365, m_bSpaces(true)1366, m_bParseQuotes(false)1367, m_bAllowKeyOnly(false)1368, m_nOrder(0)1369{ }13701371template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1372CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::~CSimpleIniTempl()1373{1374Reset();1375}13761377template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1378void1379CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Reset()1380{1381// remove all data1382delete[] m_pData;1383m_pData = NULL;1384m_uDataLen = 0;1385m_pFileComment = NULL;1386if (!m_data.empty()) {1387m_data.erase(m_data.begin(), m_data.end());1388}13891390// remove all strings1391if (!m_strings.empty()) {1392typename TNamesDepend::iterator i = m_strings.begin();1393for (; i != m_strings.end(); ++i) {1394delete[] const_cast<SI_CHAR*>(i->pItem);1395}1396m_strings.erase(m_strings.begin(), m_strings.end());1397}1398}13991400template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1401SI_Error1402CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(1403const char * a_pszFile1404)1405{1406FILE * fp = NULL;1407#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE1408fopen_s(&fp, a_pszFile, "rb");1409#else // !__STDC_WANT_SECURE_LIB__1410fp = fopen(a_pszFile, "rb");1411#endif // __STDC_WANT_SECURE_LIB__1412if (!fp) {1413return SI_FILE;1414}1415SI_Error rc = LoadFile(fp);1416fclose(fp);1417return rc;1418}14191420#ifdef SI_HAS_WIDE_FILE1421template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1422SI_Error1423CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(1424const SI_WCHAR_T * a_pwszFile1425)1426{1427#ifdef _WIN321428FILE * fp = NULL;1429#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE1430_wfopen_s(&fp, a_pwszFile, L"rb");1431#else // !__STDC_WANT_SECURE_LIB__1432fp = _wfopen(a_pwszFile, L"rb");1433#endif // __STDC_WANT_SECURE_LIB__1434if (!fp) return SI_FILE;1435SI_Error rc = LoadFile(fp);1436fclose(fp);1437return rc;1438#else // !_WIN32 (therefore SI_CONVERT_ICU)1439char szFile[256];1440u_austrncpy(szFile, a_pwszFile, sizeof(szFile));1441return LoadFile(szFile);1442#endif // _WIN321443}1444#endif // SI_HAS_WIDE_FILE14451446template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1447SI_Error1448CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(1449FILE * a_fpFile1450)1451{1452// load the raw file data1453int retval = fseek(a_fpFile, 0, SEEK_END);1454if (retval != 0) {1455return SI_FILE;1456}1457long lSize = ftell(a_fpFile);1458if (lSize < 0) {1459return SI_FILE;1460}1461if (lSize == 0) {1462return SI_OK;1463}14641465// allocate and ensure NULL terminated1466char * pData = new(std::nothrow) char[lSize+static_cast<size_t>(1)];1467if (!pData) {1468return SI_NOMEM;1469}1470pData[lSize] = 0;14711472// load data into buffer1473fseek(a_fpFile, 0, SEEK_SET);1474size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);1475if (uRead != (size_t) lSize) {1476delete[] pData;1477return SI_FILE;1478}14791480// convert the raw data to unicode1481SI_Error rc = LoadData(pData, uRead);1482delete[] pData;1483return rc;1484}14851486template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1487SI_Error1488CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(1489const char * a_pData,1490size_t a_uDataLen1491)1492{1493if (!a_pData) {1494return SI_OK;1495}14961497// if the UTF-8 BOM exists, consume it and set mode to unicode, if we have1498// already loaded data and try to change mode half-way through then this will1499// be ignored and we will assert in debug versions1500if (a_uDataLen >= 3 && memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {1501a_pData += 3;1502a_uDataLen -= 3;1503SI_ASSERT(m_bStoreIsUtf8 || !m_pData); // we don't expect mixed mode data1504SetUnicode();1505}15061507if (a_uDataLen == 0) {1508return SI_OK;1509}15101511// determine the length of the converted data1512SI_CONVERTER converter(m_bStoreIsUtf8);1513size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);1514if (uLen == (size_t)(-1)) {1515return SI_FAIL;1516}15171518// allocate memory for the data, ensure that there is a NULL1519// terminator wherever the converted data ends1520SI_CHAR * pData = new(std::nothrow) SI_CHAR[uLen+1];1521if (!pData) {1522return SI_NOMEM;1523}1524memset(pData, 0, sizeof(SI_CHAR)*(uLen+1));15251526// convert the data1527if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {1528delete[] pData;1529return SI_FAIL;1530}15311532// parse it1533const static SI_CHAR empty = 0;1534SI_CHAR * pWork = pData;1535const SI_CHAR * pSection = ∅1536const SI_CHAR * pItem = NULL;1537const SI_CHAR * pVal = NULL;1538const SI_CHAR * pComment = NULL;15391540// We copy the strings if we are loading data into this class when we1541// already have stored some.1542bool bCopyStrings = (m_pData != NULL);15431544// find a file comment if it exists, this is a comment that starts at the1545// beginning of the file and continues until the first blank line.1546SI_Error rc = FindFileComment(pWork, bCopyStrings);1547if (rc < 0) return rc;15481549// add every entry in the file to the data table1550while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {1551rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings);1552if (rc < 0) return rc;1553}15541555// store these strings if we didn't copy them1556if (bCopyStrings) {1557delete[] pData;1558}1559else {1560m_pData = pData;1561m_uDataLen = uLen+1;1562}15631564return SI_OK;1565}15661567#ifdef SI_SUPPORT_IOSTREAMS1568template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1569SI_Error1570CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(1571std::istream & a_istream1572)1573{1574std::string strData;1575char szBuf[512];1576do {1577a_istream.get(szBuf, sizeof(szBuf), '\0');1578strData.append(szBuf);1579}1580while (a_istream.good());1581return LoadData(strData);1582}1583#endif // SI_SUPPORT_IOSTREAMS15841585template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1586SI_Error1587CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindFileComment(1588SI_CHAR *& a_pData,1589bool a_bCopyStrings1590)1591{1592// there can only be a single file comment1593if (m_pFileComment) {1594return SI_OK;1595}15961597// Load the file comment as multi-line text, this will modify all of1598// the newline characters to be single \n chars1599if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {1600return SI_OK;1601}16021603// copy the string if necessary1604if (a_bCopyStrings) {1605SI_Error rc = CopyString(m_pFileComment);1606if (rc < 0) return rc;1607}16081609return SI_OK;1610}16111612template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1613bool1614CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(1615SI_CHAR *& a_pData,1616const SI_CHAR *& a_pSection,1617const SI_CHAR *& a_pKey,1618const SI_CHAR *& a_pVal,1619const SI_CHAR *& a_pComment1620) const1621{1622a_pComment = NULL;16231624bool bHaveValue = false;1625SI_CHAR * pTrail = NULL;1626while (*a_pData) {1627// skip spaces and empty lines1628while (*a_pData && IsSpace(*a_pData)) {1629++a_pData;1630}1631if (!*a_pData) {1632break;1633}16341635// skip processing of comment lines but keep a pointer to1636// the start of the comment.1637if (IsComment(*a_pData)) {1638LoadMultiLineText(a_pData, a_pComment, NULL, true);1639continue;1640}16411642// process section names1643if (*a_pData == '[') {1644// skip leading spaces1645++a_pData;1646while (*a_pData && IsSpace(*a_pData)) {1647++a_pData;1648}16491650// find the end of the section name (it may contain spaces)1651// and convert it to lowercase as necessary1652a_pSection = a_pData;1653while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {1654++a_pData;1655}16561657// if it's an invalid line, just skip it1658if (*a_pData != ']') {1659continue;1660}16611662// remove trailing spaces from the section1663pTrail = a_pData - 1;1664while (pTrail >= a_pSection && IsSpace(*pTrail)) {1665--pTrail;1666}1667++pTrail;1668*pTrail = 0;16691670// skip to the end of the line1671++a_pData; // safe as checked that it == ']' above1672while (*a_pData && !IsNewLineChar(*a_pData)) {1673++a_pData;1674}16751676a_pKey = NULL;1677a_pVal = NULL;1678return true;1679}16801681// find the end of the key name (it may contain spaces)1682a_pKey = a_pData;1683while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {1684++a_pData;1685}1686// *a_pData is null, equals, or newline16871688// if no value and we don't allow no value, then invalid1689bHaveValue = (*a_pData == '=');1690if (!bHaveValue && !m_bAllowKeyOnly) {1691continue;1692}16931694// empty keys are invalid1695if (bHaveValue && a_pKey == a_pData) {1696while (*a_pData && !IsNewLineChar(*a_pData)) {1697++a_pData;1698}1699continue;1700}17011702// remove trailing spaces from the key1703pTrail = a_pData - 1;1704while (pTrail >= a_pKey && IsSpace(*pTrail)) {1705--pTrail;1706}1707++pTrail;17081709if (bHaveValue) {1710// process the value1711*pTrail = 0;17121713// skip leading whitespace on the value1714++a_pData; // safe as checked that it == '=' above1715while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {1716++a_pData;1717}17181719// find the end of the value which is the end of this line1720a_pVal = a_pData;1721while (*a_pData && !IsNewLineChar(*a_pData)) {1722++a_pData;1723}17241725// remove trailing spaces from the value1726pTrail = a_pData - 1;1727if (*a_pData) { // prepare for the next round1728SkipNewLine(a_pData);1729}1730while (pTrail >= a_pVal && IsSpace(*pTrail)) {1731--pTrail;1732}1733++pTrail;1734*pTrail = 0;17351736// check for multi-line entries1737if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {1738// skip the "<<<" to get the tag that will end the multiline1739const SI_CHAR* pTagName = a_pVal + 3;1740return LoadMultiLineText(a_pData, a_pVal, pTagName);1741}17421743// check for quoted values, we are not supporting escapes in quoted values (yet)1744if (m_bParseQuotes) {1745--pTrail;1746if (pTrail > a_pVal && *a_pVal == '"' && *pTrail == '"') {1747++a_pVal;1748*pTrail = 0;1749}1750}1751}1752else {1753// no value to process, just prepare for the next1754if (*a_pData) {1755SkipNewLine(a_pData);1756}1757*pTrail = 0;1758}17591760// return the standard entry1761return true;1762}17631764return false;1765}17661767template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1768bool1769CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineTag(1770const SI_CHAR * a_pVal1771) const1772{1773// check for the "<<<" prefix for a multi-line entry1774if (*a_pVal++ != '<') return false;1775if (*a_pVal++ != '<') return false;1776if (*a_pVal++ != '<') return false;1777return true;1778}17791780template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1781bool1782CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData(1783const SI_CHAR * a_pData1784) const1785{1786// data is multi-line if it has any of the following features:1787// * whitespace prefix1788// * embedded newlines1789// * whitespace suffix17901791// empty string1792if (!*a_pData) {1793return false;1794}17951796// check for prefix1797if (IsSpace(*a_pData)) {1798return true;1799}18001801// embedded newlines1802while (*a_pData) {1803if (IsNewLineChar(*a_pData)) {1804return true;1805}1806++a_pData;1807}18081809// check for suffix1810if (IsSpace(*--a_pData)) {1811return true;1812}18131814return false;1815}18161817template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1818bool1819CSimpleIniTempl<SI_CHAR, SI_STRLESS, SI_CONVERTER>::IsSingleLineQuotedValue(1820const SI_CHAR* a_pData1821) const1822{1823// data needs quoting if it starts or ends with whitespace1824// and doesn't have embedded newlines18251826// empty string1827if (!*a_pData) {1828return false;1829}18301831// check for prefix1832if (IsSpace(*a_pData)) {1833return true;1834}18351836// embedded newlines1837while (*a_pData) {1838if (IsNewLineChar(*a_pData)) {1839return false;1840}1841++a_pData;1842}18431844// check for suffix1845if (IsSpace(*--a_pData)) {1846return true;1847}18481849return false;1850}18511852template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1853bool1854CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar(1855SI_CHAR a_c1856) const1857{1858return (a_c == '\n' || a_c == '\r');1859}18601861template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1862bool1863CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(1864SI_CHAR *& a_pData,1865const SI_CHAR *& a_pVal,1866const SI_CHAR * a_pTagName,1867bool a_bAllowBlankLinesInComment1868) const1869{1870// we modify this data to strip all newlines down to a single '\n'1871// character. This means that on Windows we need to strip out some1872// characters which will make the data shorter.1873// i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become1874// LINE1-LINE1\nLINE2-LINE2\01875// The pDataLine entry is the pointer to the location in memory that1876// the current line needs to start to run following the existing one.1877// This may be the same as pCurrLine in which case no move is needed.1878SI_CHAR * pDataLine = a_pData;1879SI_CHAR * pCurrLine;18801881// value starts at the current line1882a_pVal = a_pData;18831884// find the end tag. This tag must start in column 1 and be1885// followed by a newline. We ignore any whitespace after the end1886// tag but not whitespace before it.1887SI_CHAR cEndOfLineChar = *a_pData;1888for(;;) {1889// if we are loading comments then we need a comment character as1890// the first character on every line1891if (!a_pTagName && !IsComment(*a_pData)) {1892// if we aren't allowing blank lines then we're done1893if (!a_bAllowBlankLinesInComment) {1894break;1895}18961897// if we are allowing blank lines then we only include them1898// in this comment if another comment follows, so read ahead1899// to find out.1900SI_CHAR * pCurr = a_pData;1901int nNewLines = 0;1902while (IsSpace(*pCurr)) {1903if (IsNewLineChar(*pCurr)) {1904++nNewLines;1905SkipNewLine(pCurr);1906}1907else {1908++pCurr;1909}1910}19111912// we have a comment, add the blank lines to the output1913// and continue processing from here1914if (IsComment(*pCurr)) {1915for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n';1916a_pData = pCurr;1917continue;1918}19191920// the comment ends here1921break;1922}19231924// find the end of this line1925pCurrLine = a_pData;1926while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;19271928// move this line down to the location that it should be if necessary1929if (pDataLine < pCurrLine) {1930size_t nLen = (size_t) (a_pData - pCurrLine);1931memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));1932pDataLine[nLen] = '\0';1933}19341935// end the line with a NULL1936cEndOfLineChar = *a_pData;1937*a_pData = 0;19381939// if are looking for a tag then do the check now. This is done before1940// checking for end of the data, so that if we have the tag at the end1941// of the data then the tag is removed correctly.1942if (a_pTagName) {1943// strip whitespace from the end of this tag1944SI_CHAR* pc = a_pData - 1;1945while (pc > pDataLine && IsSpace(*pc)) --pc;1946SI_CHAR ch = *++pc;1947*pc = 0;19481949if (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)) {1950break;1951}19521953*pc = ch;1954}19551956// if we are at the end of the data then we just automatically end1957// this entry and return the current data.1958if (!cEndOfLineChar) {1959return true;1960}19611962// otherwise we need to process this newline to ensure that it consists1963// of just a single \n character.1964pDataLine += (a_pData - pCurrLine);1965*a_pData = cEndOfLineChar;1966SkipNewLine(a_pData);1967*pDataLine++ = '\n';1968}19691970// if we didn't find a comment at all then return false1971if (a_pVal == a_pData) {1972a_pVal = NULL;1973return false;1974}19751976// the data (which ends at the end of the last line) needs to be1977// null-terminated BEFORE before the newline character(s). If the1978// user wants a new line in the multi-line data then they need to1979// add an empty line before the tag.1980*--pDataLine = '\0';19811982// if looking for a tag and if we aren't at the end of the data,1983// then move a_pData to the start of the next line.1984if (a_pTagName && cEndOfLineChar) {1985SI_ASSERT(IsNewLineChar(cEndOfLineChar));1986*a_pData = cEndOfLineChar;1987SkipNewLine(a_pData);1988}19891990return true;1991}19921993template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>1994SI_Error1995CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString(1996const SI_CHAR *& a_pString1997)1998{1999size_t uLen = 0;2000if (sizeof(SI_CHAR) == sizeof(char)) {2001uLen = strlen((const char *)a_pString);2002}2003else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {2004uLen = wcslen((const wchar_t *)a_pString);2005}2006else {2007for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;2008}2009++uLen; // NULL character2010SI_CHAR * pCopy = new(std::nothrow) SI_CHAR[uLen];2011if (!pCopy) {2012return SI_NOMEM;2013}2014memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);2015m_strings.push_back(pCopy);2016a_pString = pCopy;2017return SI_OK;2018}20192020template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2021SI_Error2022CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(2023const SI_CHAR * a_pSection,2024const SI_CHAR * a_pKey,2025const SI_CHAR * a_pValue,2026const SI_CHAR * a_pComment,2027bool a_bForceReplace,2028bool a_bCopyStrings2029)2030{2031SI_Error rc;2032bool bInserted = false;20332034SI_ASSERT(!a_pComment || IsComment(*a_pComment));20352036// if we are copying strings then make a copy of the comment now2037// because we will need it when we add the entry.2038if (a_bCopyStrings && a_pComment) {2039rc = CopyString(a_pComment);2040if (rc < 0) return rc;2041}20422043// create the section entry if necessary2044typename TSection::iterator iSection = m_data.find(a_pSection);2045if (iSection == m_data.end()) {2046// if the section doesn't exist then we need a copy as the2047// string needs to last beyond the end of this function2048if (a_bCopyStrings) {2049rc = CopyString(a_pSection);2050if (rc < 0) return rc;2051}20522053// only set the comment if this is a section only entry2054Entry oSection(a_pSection, ++m_nOrder);2055if (a_pComment && !a_pKey) {2056oSection.pComment = a_pComment;2057}20582059typename TSection::value_type oEntry(oSection, TKeyVal());2060typedef typename TSection::iterator SectionIterator;2061std::pair<SectionIterator,bool> i = m_data.insert(oEntry);2062iSection = i.first;2063bInserted = true;2064}2065if (!a_pKey) {2066// section only entries are specified with pItem as NULL2067return bInserted ? SI_INSERTED : SI_UPDATED;2068}20692070// check for existence of the key2071TKeyVal & keyval = iSection->second;2072typename TKeyVal::iterator iKey = keyval.find(a_pKey);2073bInserted = iKey == keyval.end();20742075// remove all existing entries but save the load order and2076// comment of the first entry2077int nLoadOrder = ++m_nOrder;2078if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) {2079const SI_CHAR * pComment = NULL;2080while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) {2081if (iKey->first.nOrder < nLoadOrder) {2082nLoadOrder = iKey->first.nOrder;2083pComment = iKey->first.pComment;2084}2085++iKey;2086}2087if (pComment) {2088DeleteString(a_pComment);2089a_pComment = pComment;2090CopyString(a_pComment);2091}2092Delete(a_pSection, a_pKey);2093iKey = keyval.end();2094}20952096// values need to be a valid string, even if they are an empty string2097if (!a_pValue) {2098a_pValue = &m_cEmptyString;2099}21002101// make string copies if necessary2102bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;2103if (a_bCopyStrings) {2104if (bForceCreateNewKey || iKey == keyval.end()) {2105// if the key doesn't exist then we need a copy as the2106// string needs to last beyond the end of this function2107// because we will be inserting the key next2108rc = CopyString(a_pKey);2109if (rc < 0) return rc;2110}21112112// we always need a copy of the value2113rc = CopyString(a_pValue);2114if (rc < 0) return rc;2115}21162117// create the key entry2118if (iKey == keyval.end() || bForceCreateNewKey) {2119Entry oKey(a_pKey, nLoadOrder);2120if (a_pComment) {2121oKey.pComment = a_pComment;2122}2123typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL));2124iKey = keyval.insert(oEntry);2125}21262127iKey->second = a_pValue;2128return bInserted ? SI_INSERTED : SI_UPDATED;2129}21302131template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2132const SI_CHAR *2133CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetValue(2134const SI_CHAR * a_pSection,2135const SI_CHAR * a_pKey,2136const SI_CHAR * a_pDefault,2137bool * a_pHasMultiple2138) const2139{2140if (a_pHasMultiple) {2141*a_pHasMultiple = false;2142}2143if (!a_pSection || !a_pKey) {2144return a_pDefault;2145}2146typename TSection::const_iterator iSection = m_data.find(a_pSection);2147if (iSection == m_data.end()) {2148return a_pDefault;2149}2150typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);2151if (iKeyVal == iSection->second.end()) {2152return a_pDefault;2153}21542155// check for multiple entries with the same key2156if (m_bAllowMultiKey && a_pHasMultiple) {2157typename TKeyVal::const_iterator iTemp = iKeyVal;2158if (++iTemp != iSection->second.end()) {2159if (!IsLess(a_pKey, iTemp->first.pItem)) {2160*a_pHasMultiple = true;2161}2162}2163}21642165return iKeyVal->second;2166}21672168template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2169long2170CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetLongValue(2171const SI_CHAR * a_pSection,2172const SI_CHAR * a_pKey,2173long a_nDefault,2174bool * a_pHasMultiple2175) const2176{2177// return the default if we don't have a value2178const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);2179if (!pszValue || !*pszValue) return a_nDefault;21802181// convert to UTF-8/MBCS which for a numeric value will be the same as ASCII2182char szValue[64] = { 0 };2183SI_CONVERTER c(m_bStoreIsUtf8);2184if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {2185return a_nDefault;2186}21872188// handle the value as hex if prefaced with "0x"2189long nValue = a_nDefault;2190char * pszSuffix = szValue;2191if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {2192if (!szValue[2]) return a_nDefault;2193nValue = strtol(&szValue[2], &pszSuffix, 16);2194}2195else {2196nValue = strtol(szValue, &pszSuffix, 10);2197}21982199// any invalid strings will return the default value2200if (*pszSuffix) {2201return a_nDefault;2202}22032204return nValue;2205}22062207template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2208SI_Error2209CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue(2210const SI_CHAR * a_pSection,2211const SI_CHAR * a_pKey,2212long a_nValue,2213const SI_CHAR * a_pComment,2214bool a_bUseHex,2215bool a_bForceReplace2216)2217{2218// use SetValue to create sections2219if (!a_pSection || !a_pKey) return SI_FAIL;22202221// convert to an ASCII string2222char szInput[64];2223#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE2224sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);2225#else // !__STDC_WANT_SECURE_LIB__2226snprintf(szInput, sizeof(szInput), a_bUseHex ? "0x%lx" : "%ld", a_nValue);2227#endif // __STDC_WANT_SECURE_LIB__22282229// convert to output text2230SI_CHAR szOutput[64];2231SI_CONVERTER c(m_bStoreIsUtf8);2232c.ConvertFromStore(szInput, strlen(szInput) + 1,2233szOutput, sizeof(szOutput) / sizeof(SI_CHAR));22342235// actually add it2236return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);2237}22382239template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2240double2241CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetDoubleValue(2242const SI_CHAR * a_pSection,2243const SI_CHAR * a_pKey,2244double a_nDefault,2245bool * a_pHasMultiple2246) const2247{2248// return the default if we don't have a value2249const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);2250if (!pszValue || !*pszValue) return a_nDefault;22512252// convert to UTF-8/MBCS which for a numeric value will be the same as ASCII2253char szValue[64] = { 0 };2254SI_CONVERTER c(m_bStoreIsUtf8);2255if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {2256return a_nDefault;2257}22582259char * pszSuffix = NULL;2260double nValue = strtod(szValue, &pszSuffix);22612262// any invalid strings will return the default value2263if (!pszSuffix || *pszSuffix) {2264return a_nDefault;2265}22662267return nValue;2268}22692270template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2271SI_Error2272CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetDoubleValue(2273const SI_CHAR * a_pSection,2274const SI_CHAR * a_pKey,2275double a_nValue,2276const SI_CHAR * a_pComment,2277bool a_bForceReplace2278)2279{2280// use SetValue to create sections2281if (!a_pSection || !a_pKey) return SI_FAIL;22822283// convert to an ASCII string2284char szInput[64];2285#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE2286sprintf_s(szInput, "%f", a_nValue);2287#else // !__STDC_WANT_SECURE_LIB__2288snprintf(szInput, sizeof(szInput), "%f", a_nValue);2289#endif // __STDC_WANT_SECURE_LIB__22902291// convert to output text2292SI_CHAR szOutput[64];2293SI_CONVERTER c(m_bStoreIsUtf8);2294c.ConvertFromStore(szInput, strlen(szInput) + 1,2295szOutput, sizeof(szOutput) / sizeof(SI_CHAR));22962297// actually add it2298return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);2299}23002301template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2302bool2303CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetBoolValue(2304const SI_CHAR * a_pSection,2305const SI_CHAR * a_pKey,2306bool a_bDefault,2307bool * a_pHasMultiple2308) const2309{2310// return the default if we don't have a value2311const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);2312if (!pszValue || !*pszValue) return a_bDefault;23132314// we only look at the minimum number of characters2315switch (pszValue[0]) {2316case 't': case 'T': // true2317case 'y': case 'Y': // yes2318case '1': // 1 (one)2319return true;23202321case 'f': case 'F': // false2322case 'n': case 'N': // no2323case '0': // 0 (zero)2324return false;23252326case 'o': case 'O':2327if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on2328if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off2329break;2330}23312332// no recognized value, return the default2333return a_bDefault;2334}23352336template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2337SI_Error2338CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetBoolValue(2339const SI_CHAR * a_pSection,2340const SI_CHAR * a_pKey,2341bool a_bValue,2342const SI_CHAR * a_pComment,2343bool a_bForceReplace2344)2345{2346// use SetValue to create sections2347if (!a_pSection || !a_pKey) return SI_FAIL;23482349// convert to an ASCII string2350const char * pszInput = a_bValue ? "true" : "false";23512352// convert to output text2353SI_CHAR szOutput[64];2354SI_CONVERTER c(m_bStoreIsUtf8);2355c.ConvertFromStore(pszInput, strlen(pszInput) + 1,2356szOutput, sizeof(szOutput) / sizeof(SI_CHAR));23572358// actually add it2359return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);2360}23612362template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2363bool2364CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues(2365const SI_CHAR * a_pSection,2366const SI_CHAR * a_pKey,2367TNamesDepend & a_values2368) const2369{2370a_values.clear();23712372if (!a_pSection || !a_pKey) {2373return false;2374}2375typename TSection::const_iterator iSection = m_data.find(a_pSection);2376if (iSection == m_data.end()) {2377return false;2378}2379typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);2380if (iKeyVal == iSection->second.end()) {2381return false;2382}23832384// insert all values for this key2385a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));2386if (m_bAllowMultiKey) {2387++iKeyVal;2388while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {2389a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));2390++iKeyVal;2391}2392}23932394return true;2395}23962397template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2398int2399CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSectionSize(2400const SI_CHAR * a_pSection2401) const2402{2403if (!a_pSection) {2404return -1;2405}24062407typename TSection::const_iterator iSection = m_data.find(a_pSection);2408if (iSection == m_data.end()) {2409return -1;2410}2411const TKeyVal & section = iSection->second;24122413// if multi-key isn't permitted then the section size is2414// the number of keys that we have.2415if (!m_bAllowMultiKey || section.empty()) {2416return (int) section.size();2417}24182419// otherwise we need to count them2420int nCount = 0;2421const SI_CHAR * pLastKey = NULL;2422typename TKeyVal::const_iterator iKeyVal = section.begin();2423for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {2424if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {2425++nCount;2426pLastKey = iKeyVal->first.pItem;2427}2428}2429return nCount;2430}24312432template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2433const typename CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::TKeyVal *2434CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSection(2435const SI_CHAR * a_pSection2436) const2437{2438if (a_pSection) {2439typename TSection::const_iterator i = m_data.find(a_pSection);2440if (i != m_data.end()) {2441return &(i->second);2442}2443}2444return 0;2445}24462447template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2448size_t2449CSimpleIniTempl<SI_CHAR, SI_STRLESS, SI_CONVERTER>::GetKeyCount() const2450{2451size_t count = 0;2452typename TSection::const_iterator i = m_data.begin();2453for (; i != m_data.end(); ++i)2454count += i->second.size();2455return count;2456}24572458template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2459void2460CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections(2461TNamesDepend & a_names2462) const2463{2464a_names.clear();2465typename TSection::const_iterator i = m_data.begin();2466for (int n = 0; i != m_data.end(); ++i, ++n ) {2467a_names.push_back(i->first);2468}2469}24702471template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2472bool2473CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllKeys(2474const SI_CHAR * a_pSection,2475TNamesDepend & a_names2476) const2477{2478a_names.clear();24792480if (!a_pSection) {2481return false;2482}24832484typename TSection::const_iterator iSection = m_data.find(a_pSection);2485if (iSection == m_data.end()) {2486return false;2487}24882489const TKeyVal & section = iSection->second;2490const SI_CHAR * pLastKey = NULL;2491typename TKeyVal::const_iterator iKeyVal = section.begin();2492for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) {2493if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {2494a_names.push_back(iKeyVal->first);2495pLastKey = iKeyVal->first.pItem;2496}2497}24982499return true;2500}25012502template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2503SI_Error2504CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(2505const char * a_pszFile,2506bool a_bAddSignature2507) const2508{2509FILE * fp = NULL;2510#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE2511fopen_s(&fp, a_pszFile, "wb");2512#else // !__STDC_WANT_SECURE_LIB__2513fp = fopen(a_pszFile, "wb");2514#endif // __STDC_WANT_SECURE_LIB__2515if (!fp) return SI_FILE;2516SI_Error rc = SaveFile(fp, a_bAddSignature);2517fclose(fp);2518return rc;2519}25202521#ifdef SI_HAS_WIDE_FILE2522template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2523SI_Error2524CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(2525const SI_WCHAR_T * a_pwszFile,2526bool a_bAddSignature2527) const2528{2529#ifdef _WIN322530FILE * fp = NULL;2531#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE2532_wfopen_s(&fp, a_pwszFile, L"wb");2533#else // !__STDC_WANT_SECURE_LIB__2534fp = _wfopen(a_pwszFile, L"wb");2535#endif // __STDC_WANT_SECURE_LIB__2536if (!fp) return SI_FILE;2537SI_Error rc = SaveFile(fp, a_bAddSignature);2538fclose(fp);2539return rc;2540#else // !_WIN32 (therefore SI_CONVERT_ICU)2541char szFile[256];2542u_austrncpy(szFile, a_pwszFile, sizeof(szFile));2543return SaveFile(szFile, a_bAddSignature);2544#endif // _WIN322545}2546#endif // SI_HAS_WIDE_FILE25472548template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2549SI_Error2550CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(2551FILE * a_pFile,2552bool a_bAddSignature2553) const2554{2555FileWriter writer(a_pFile);2556return Save(writer, a_bAddSignature);2557}25582559template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2560SI_Error2561CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(2562OutputWriter & a_oOutput,2563bool a_bAddSignature2564) const2565{2566Converter convert(m_bStoreIsUtf8);25672568// add the UTF-8 signature if it is desired2569if (m_bStoreIsUtf8 && a_bAddSignature) {2570a_oOutput.Write(SI_UTF8_SIGNATURE);2571}25722573// get all of the sections sorted in load order2574TNamesDepend oSections;2575GetAllSections(oSections);2576#if defined(_MSC_VER) && _MSC_VER <= 12002577oSections.sort();2578#elif defined(__BORLANDC__)2579oSections.sort(Entry::LoadOrder());2580#else2581oSections.sort(typename Entry::LoadOrder());2582#endif25832584// if there is an empty section name, then it must be written out first2585// regardless of the load order2586typename TNamesDepend::iterator is = oSections.begin();2587for (; is != oSections.end(); ++is) {2588if (!*is->pItem) {2589// move the empty section name to the front of the section list2590if (is != oSections.begin()) {2591oSections.splice(oSections.begin(), oSections, is, std::next(is));2592}2593break;2594}2595}25962597// write the file comment if we have one2598bool bNeedNewLine = false;2599if (m_pFileComment) {2600if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {2601return SI_FAIL;2602}2603bNeedNewLine = true;2604}26052606// iterate through our sections and output the data2607typename TNamesDepend::const_iterator iSection = oSections.begin();2608for ( ; iSection != oSections.end(); ++iSection ) {2609// write out the comment if there is one2610if (iSection->pComment) {2611if (bNeedNewLine) {2612a_oOutput.Write(SI_NEWLINE_A);2613a_oOutput.Write(SI_NEWLINE_A);2614}2615if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) {2616return SI_FAIL;2617}2618bNeedNewLine = false;2619}26202621if (bNeedNewLine) {2622a_oOutput.Write(SI_NEWLINE_A);2623a_oOutput.Write(SI_NEWLINE_A);2624bNeedNewLine = false;2625}26262627// write the section (unless there is no section name)2628if (*iSection->pItem) {2629if (!convert.ConvertToStore(iSection->pItem)) {2630return SI_FAIL;2631}2632a_oOutput.Write("[");2633a_oOutput.Write(convert.Data());2634a_oOutput.Write("]");2635a_oOutput.Write(SI_NEWLINE_A);2636}26372638// get all of the keys sorted in load order2639TNamesDepend oKeys;2640GetAllKeys(iSection->pItem, oKeys);2641#if defined(_MSC_VER) && _MSC_VER <= 12002642oKeys.sort();2643#elif defined(__BORLANDC__)2644oKeys.sort(Entry::LoadOrder());2645#else2646oKeys.sort(typename Entry::LoadOrder());2647#endif26482649// write all keys and values2650typename TNamesDepend::const_iterator iKey = oKeys.begin();2651for ( ; iKey != oKeys.end(); ++iKey) {2652// get all values for this key2653TNamesDepend oValues;2654GetAllValues(iSection->pItem, iKey->pItem, oValues);26552656typename TNamesDepend::const_iterator iValue = oValues.begin();2657for ( ; iValue != oValues.end(); ++iValue) {2658// write out the comment if there is one2659if (iValue->pComment) {2660a_oOutput.Write(SI_NEWLINE_A);2661if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) {2662return SI_FAIL;2663}2664}26652666// write the key2667if (!convert.ConvertToStore(iKey->pItem)) {2668return SI_FAIL;2669}2670a_oOutput.Write(convert.Data());26712672// write the value as long2673if (*iValue->pItem || !m_bAllowKeyOnly) {2674if (!convert.ConvertToStore(iValue->pItem)) {2675return SI_FAIL;2676}2677a_oOutput.Write(m_bSpaces ? " = " : "=");2678if (m_bParseQuotes && IsSingleLineQuotedValue(iValue->pItem)) {2679// the only way to preserve external whitespace on a value (i.e. before or after)2680// is to quote it. This is simple quoting, we don't escape quotes within the data.2681a_oOutput.Write("\"");2682a_oOutput.Write(convert.Data());2683a_oOutput.Write("\"");2684}2685else if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {2686// multi-line data needs to be processed specially to ensure2687// that we use the correct newline format for the current system2688a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);2689if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {2690return SI_FAIL;2691}2692a_oOutput.Write("END_OF_TEXT");2693}2694else {2695a_oOutput.Write(convert.Data());2696}2697}2698a_oOutput.Write(SI_NEWLINE_A);2699}2700}27012702bNeedNewLine = true;2703}27042705return SI_OK;2706}27072708template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2709bool2710CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::OutputMultiLineText(2711OutputWriter & a_oOutput,2712Converter & a_oConverter,2713const SI_CHAR * a_pText2714) const2715{2716const SI_CHAR * pEndOfLine;2717SI_CHAR cEndOfLineChar = *a_pText;2718while (cEndOfLineChar) {2719// find the end of this line2720pEndOfLine = a_pText;2721for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;2722cEndOfLineChar = *pEndOfLine;27232724// temporarily null terminate, convert and output the line2725*const_cast<SI_CHAR*>(pEndOfLine) = 0;2726if (!a_oConverter.ConvertToStore(a_pText)) {2727return false;2728}2729*const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar;2730a_pText += (pEndOfLine - a_pText) + 1;2731a_oOutput.Write(a_oConverter.Data());2732a_oOutput.Write(SI_NEWLINE_A);2733}2734return true;2735}27362737template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2738bool2739CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(2740const SI_CHAR * a_pSection,2741const SI_CHAR * a_pKey,2742bool a_bRemoveEmpty2743)2744{2745return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty);2746}27472748template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2749bool2750CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteValue(2751const SI_CHAR * a_pSection,2752const SI_CHAR * a_pKey,2753const SI_CHAR * a_pValue,2754bool a_bRemoveEmpty2755)2756{2757if (!a_pSection) {2758return false;2759}27602761typename TSection::iterator iSection = m_data.find(a_pSection);2762if (iSection == m_data.end()) {2763return false;2764}27652766// remove a single key if we have a keyname2767if (a_pKey) {2768typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);2769if (iKeyVal == iSection->second.end()) {2770return false;2771}27722773const static SI_STRLESS isLess = SI_STRLESS();27742775// remove any copied strings and then the key2776typename TKeyVal::iterator iDelete;2777bool bDeleted = false;2778do {2779iDelete = iKeyVal++;27802781if(a_pValue == NULL ||2782(isLess(a_pValue, iDelete->second) == false &&2783isLess(iDelete->second, a_pValue) == false)) {2784DeleteString(iDelete->first.pItem);2785DeleteString(iDelete->second);2786iSection->second.erase(iDelete);2787bDeleted = true;2788}2789}2790while (iKeyVal != iSection->second.end()2791&& !IsLess(a_pKey, iKeyVal->first.pItem));27922793if(!bDeleted) {2794return false;2795}27962797// done now if the section is not empty or we are not pruning away2798// the empty sections. Otherwise let it fall through into the section2799// deletion code2800if (!a_bRemoveEmpty || !iSection->second.empty()) {2801return true;2802}2803}2804else {2805// delete all copied strings from this section. The actual2806// entries will be removed when the section is removed.2807typename TKeyVal::iterator iKeyVal = iSection->second.begin();2808for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {2809DeleteString(iKeyVal->first.pItem);2810DeleteString(iKeyVal->second);2811}2812}28132814// delete the section itself2815DeleteString(iSection->first.pItem);2816m_data.erase(iSection);28172818return true;2819}28202821template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>2822void2823CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString(2824const SI_CHAR * a_pString2825)2826{2827// strings may exist either inside the data block, or they will be2828// individually allocated and stored in m_strings. We only physically2829// delete those stored in m_strings.2830if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {2831typename TNamesDepend::iterator i = m_strings.begin();2832for (;i != m_strings.end(); ++i) {2833if (a_pString == i->pItem) {2834delete[] const_cast<SI_CHAR*>(i->pItem);2835m_strings.erase(i);2836break;2837}2838}2839}2840}28412842// ---------------------------------------------------------------------------2843// CONVERSION FUNCTIONS2844// ---------------------------------------------------------------------------28452846// Defines the conversion classes for different libraries. Before including2847// SimpleIni.h, set the converter that you wish you use by defining one of the2848// following symbols.2849//2850// SI_NO_CONVERSION Do not make the "W" wide character version of the2851// library available. Only CSimpleIniA etc is defined.2852// SI_CONVERT_GENERIC Use the Unicode reference conversion library in2853// the accompanying files ConvertUTF.h/c2854// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires2855// ICU headers on include path and icuuc.lib2856// SI_CONVERT_WIN32 Use the Win32 API functions for conversion.28572858#if !defined(SI_NO_CONVERSION) && !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)2859# ifdef _WIN322860# define SI_CONVERT_WIN322861# else2862# define SI_CONVERT_GENERIC2863# endif2864#endif28652866/**2867* Generic case-sensitive less than comparison. This class returns numerically2868* ordered ASCII case-sensitive text for all possible sizes and types of2869* SI_CHAR.2870*/2871template<class SI_CHAR>2872struct SI_GenericCase {2873bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {2874long cmp;2875for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {2876cmp = (long) *pLeft - (long) *pRight;2877if (cmp != 0) {2878return cmp < 0;2879}2880}2881return *pRight != 0;2882}2883};28842885/**2886* Generic ASCII case-insensitive less than comparison. This class returns2887* numerically ordered ASCII case-insensitive text for all possible sizes2888* and types of SI_CHAR. It is not safe for MBCS text comparison where2889* ASCII A-Z characters are used in the encoding of multi-byte characters.2890*/2891template<class SI_CHAR>2892struct SI_GenericNoCase {2893inline SI_CHAR locase(SI_CHAR ch) const {2894return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');2895}2896bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {2897long cmp;2898for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {2899cmp = (long) locase(*pLeft) - (long) locase(*pRight);2900if (cmp != 0) {2901return cmp < 0;2902}2903}2904return *pRight != 0;2905}2906};29072908/**2909* Null conversion class for MBCS/UTF-8 to char (or equivalent).2910*/2911template<class SI_CHAR>2912class SI_ConvertA {2913bool m_bStoreIsUtf8;2914protected:2915SI_ConvertA() { }2916public:2917SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }29182919/* copy and assignment */2920SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }2921SI_ConvertA & operator=(const SI_ConvertA & rhs) {2922m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;2923return *this;2924}29252926/** Calculate the number of SI_CHAR required for converting the input2927* from the storage format. The storage format is always UTF-8 or MBCS.2928*2929* @param a_pInputData Data in storage format to be converted to SI_CHAR.2930* @param a_uInputDataLen Length of storage format data in bytes. This2931* must be the actual length of the data, including2932* NULL byte if NULL terminated string is required.2933* @return Number of SI_CHAR required by the string when2934* converted. If there are embedded NULL bytes in the2935* input data, only the string up and not including2936* the NULL byte will be converted.2937* @return -1 cast to size_t on a conversion error.2938*/2939size_t SizeFromStore(2940const char * a_pInputData,2941size_t a_uInputDataLen)2942{2943(void)a_pInputData;2944SI_ASSERT(a_uInputDataLen != (size_t) -1);29452946// ASCII/MBCS/UTF-8 needs no conversion2947return a_uInputDataLen;2948}29492950/** Convert the input string from the storage format to SI_CHAR.2951* The storage format is always UTF-8 or MBCS.2952*2953* @param a_pInputData Data in storage format to be converted to SI_CHAR.2954* @param a_uInputDataLen Length of storage format data in bytes. This2955* must be the actual length of the data, including2956* NULL byte if NULL terminated string is required.2957* @param a_pOutputData Pointer to the output buffer to received the2958* converted data.2959* @param a_uOutputDataSize Size of the output buffer in SI_CHAR.2960* @return true if all of the input data was successfully2961* converted.2962*/2963bool ConvertFromStore(2964const char * a_pInputData,2965size_t a_uInputDataLen,2966SI_CHAR * a_pOutputData,2967size_t a_uOutputDataSize)2968{2969// ASCII/MBCS/UTF-8 needs no conversion2970if (a_uInputDataLen > a_uOutputDataSize) {2971return false;2972}2973memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);2974return true;2975}29762977/** Calculate the number of char required by the storage format of this2978* data. The storage format is always UTF-8 or MBCS.2979*2980* @param a_pInputData NULL terminated string to calculate the number of2981* bytes required to be converted to storage format.2982* @return Number of bytes required by the string when2983* converted to storage format. This size always2984* includes space for the terminating NULL character.2985* @return -1 cast to size_t on a conversion error.2986*/2987size_t SizeToStore(2988const SI_CHAR * a_pInputData)2989{2990// ASCII/MBCS/UTF-8 needs no conversion2991return strlen((const char *)a_pInputData) + 1;2992}29932994/** Convert the input string to the storage format of this data.2995* The storage format is always UTF-8 or MBCS.2996*2997* @param a_pInputData NULL terminated source string to convert. All of2998* the data will be converted including the2999* terminating NULL character.3000* @param a_pOutputData Pointer to the buffer to receive the converted3001* string.3002* @param a_uOutputDataSize Size of the output buffer in char.3003* @return true if all of the input data, including the3004* terminating NULL character was successfully3005* converted.3006*/3007bool ConvertToStore(3008const SI_CHAR * a_pInputData,3009char * a_pOutputData,3010size_t a_uOutputDataSize)3011{3012// calc input string length (SI_CHAR type and size independent)3013size_t uInputLen = strlen((const char *)a_pInputData) + 1;3014if (uInputLen > a_uOutputDataSize) {3015return false;3016}30173018// ascii/UTF-8 needs no conversion3019memcpy(a_pOutputData, a_pInputData, uInputLen);3020return true;3021}3022};302330243025// ---------------------------------------------------------------------------3026// SI_CONVERT_GENERIC3027// ---------------------------------------------------------------------------3028#ifdef SI_CONVERT_GENERIC30293030#define SI_Case SI_GenericCase3031#define SI_NoCase SI_GenericNoCase30323033#include <wchar.h>3034#include "ConvertUTF.h"30353036/**3037* Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference3038* library functions. This can be used on all platforms.3039*/3040template<class SI_CHAR>3041class SI_ConvertW {3042bool m_bStoreIsUtf8;3043protected:3044SI_ConvertW() { }3045public:3046SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }30473048/* copy and assignment */3049SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }3050SI_ConvertW & operator=(const SI_ConvertW & rhs) {3051m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;3052return *this;3053}30543055/** Calculate the number of SI_CHAR required for converting the input3056* from the storage format. The storage format is always UTF-8 or MBCS.3057*3058* @param a_pInputData Data in storage format to be converted to SI_CHAR.3059* @param a_uInputDataLen Length of storage format data in bytes. This3060* must be the actual length of the data, including3061* NULL byte if NULL terminated string is required.3062* @return Number of SI_CHAR required by the string when3063* converted. If there are embedded NULL bytes in the3064* input data, only the string up and not including3065* the NULL byte will be converted.3066* @return -1 cast to size_t on a conversion error.3067*/3068size_t SizeFromStore(3069const char * a_pInputData,3070size_t a_uInputDataLen)3071{3072SI_ASSERT(a_uInputDataLen != (size_t) -1);30733074if (m_bStoreIsUtf8) {3075// worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t3076// so we just return the same number of characters required as for3077// the source text.3078return a_uInputDataLen;3079}30803081#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux))3082// fall back processing for platforms that don't support a NULL dest to mbstowcs3083// worst case scenario is 1:1, this will be a sufficient buffer size3084(void)a_pInputData;3085return a_uInputDataLen;3086#else3087// get the actual required buffer size3088return mbstowcs(NULL, a_pInputData, a_uInputDataLen);3089#endif3090}30913092/** Convert the input string from the storage format to SI_CHAR.3093* The storage format is always UTF-8 or MBCS.3094*3095* @param a_pInputData Data in storage format to be converted to SI_CHAR.3096* @param a_uInputDataLen Length of storage format data in bytes. This3097* must be the actual length of the data, including3098* NULL byte if NULL terminated string is required.3099* @param a_pOutputData Pointer to the output buffer to received the3100* converted data.3101* @param a_uOutputDataSize Size of the output buffer in SI_CHAR.3102* @return true if all of the input data was successfully3103* converted.3104*/3105bool ConvertFromStore(3106const char * a_pInputData,3107size_t a_uInputDataLen,3108SI_CHAR * a_pOutputData,3109size_t a_uOutputDataSize)3110{3111if (m_bStoreIsUtf8) {3112// This uses the Unicode reference implementation to do the3113// conversion from UTF-8 to wchar_t. The required files are3114// ConvertUTF.h and ConvertUTF.c which should be included in3115// the distribution but are publicly available from unicode.org3116// at http://www.unicode.org/Public/PROGRAMS/CVTUTF/3117ConversionResult retval;3118const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;3119if (sizeof(wchar_t) == sizeof(UTF32)) {3120UTF32 * pUtf32 = (UTF32 *) a_pOutputData;3121retval = ConvertUTF8toUTF32(3122&pUtf8, pUtf8 + a_uInputDataLen,3123&pUtf32, pUtf32 + a_uOutputDataSize,3124lenientConversion);3125}3126else if (sizeof(wchar_t) == sizeof(UTF16)) {3127UTF16 * pUtf16 = (UTF16 *) a_pOutputData;3128retval = ConvertUTF8toUTF16(3129&pUtf8, pUtf8 + a_uInputDataLen,3130&pUtf16, pUtf16 + a_uOutputDataSize,3131lenientConversion);3132}3133return retval == conversionOK;3134}31353136// convert to wchar_t3137size_t retval = mbstowcs(a_pOutputData,3138a_pInputData, a_uOutputDataSize);3139return retval != (size_t)(-1);3140}31413142/** Calculate the number of char required by the storage format of this3143* data. The storage format is always UTF-8 or MBCS.3144*3145* @param a_pInputData NULL terminated string to calculate the number of3146* bytes required to be converted to storage format.3147* @return Number of bytes required by the string when3148* converted to storage format. This size always3149* includes space for the terminating NULL character.3150* @return -1 cast to size_t on a conversion error.3151*/3152size_t SizeToStore(3153const SI_CHAR * a_pInputData)3154{3155if (m_bStoreIsUtf8) {3156// worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char3157size_t uLen = 0;3158while (a_pInputData[uLen]) {3159++uLen;3160}3161return (6 * uLen) + 1;3162}3163else {3164size_t uLen = wcstombs(NULL, a_pInputData, 0);3165if (uLen == (size_t)(-1)) {3166return uLen;3167}3168return uLen + 1; // include NULL terminator3169}3170}31713172/** Convert the input string to the storage format of this data.3173* The storage format is always UTF-8 or MBCS.3174*3175* @param a_pInputData NULL terminated source string to convert. All of3176* the data will be converted including the3177* terminating NULL character.3178* @param a_pOutputData Pointer to the buffer to receive the converted3179* string.3180* @param a_uOutputDataSize Size of the output buffer in char.3181* @return true if all of the input data, including the3182* terminating NULL character was successfully3183* converted.3184*/3185bool ConvertToStore(3186const SI_CHAR * a_pInputData,3187char * a_pOutputData,3188size_t a_uOutputDataSize3189)3190{3191if (m_bStoreIsUtf8) {3192// calc input string length (SI_CHAR type and size independent)3193size_t uInputLen = 0;3194while (a_pInputData[uInputLen]) {3195++uInputLen;3196}3197++uInputLen; // include the NULL char31983199// This uses the Unicode reference implementation to do the3200// conversion from wchar_t to UTF-8. The required files are3201// ConvertUTF.h and ConvertUTF.c which should be included in3202// the distribution but are publicly available from unicode.org3203// at http://www.unicode.org/Public/PROGRAMS/CVTUTF/3204ConversionResult retval;3205UTF8 * pUtf8 = (UTF8 *) a_pOutputData;3206if (sizeof(wchar_t) == sizeof(UTF32)) {3207const UTF32 * pUtf32 = (const UTF32 *) a_pInputData;3208retval = ConvertUTF32toUTF8(3209&pUtf32, pUtf32 + uInputLen,3210&pUtf8, pUtf8 + a_uOutputDataSize,3211lenientConversion);3212}3213else if (sizeof(wchar_t) == sizeof(UTF16)) {3214const UTF16 * pUtf16 = (const UTF16 *) a_pInputData;3215retval = ConvertUTF16toUTF8(3216&pUtf16, pUtf16 + uInputLen,3217&pUtf8, pUtf8 + a_uOutputDataSize,3218lenientConversion);3219}3220return retval == conversionOK;3221}3222else {3223size_t retval = wcstombs(a_pOutputData,3224a_pInputData, a_uOutputDataSize);3225return retval != (size_t) -1;3226}3227}3228};32293230#endif // SI_CONVERT_GENERIC323132323233// ---------------------------------------------------------------------------3234// SI_CONVERT_ICU3235// ---------------------------------------------------------------------------3236#ifdef SI_CONVERT_ICU32373238#define SI_Case SI_GenericCase3239#define SI_NoCase SI_GenericNoCase32403241#include <unicode/ucnv.h>32423243/**3244* Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms.3245*/3246template<class SI_CHAR>3247class SI_ConvertW {3248const char * m_pEncoding;3249UConverter * m_pConverter;3250protected:3251SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }3252public:3253SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {3254m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;3255}32563257/* copy and assignment */3258SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }3259SI_ConvertW & operator=(const SI_ConvertW & rhs) {3260m_pEncoding = rhs.m_pEncoding;3261m_pConverter = NULL;3262return *this;3263}3264~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); }32653266/** Calculate the number of UChar required for converting the input3267* from the storage format. The storage format is always UTF-8 or MBCS.3268*3269* @param a_pInputData Data in storage format to be converted to UChar.3270* @param a_uInputDataLen Length of storage format data in bytes. This3271* must be the actual length of the data, including3272* NULL byte if NULL terminated string is required.3273* @return Number of UChar required by the string when3274* converted. If there are embedded NULL bytes in the3275* input data, only the string up and not including3276* the NULL byte will be converted.3277* @return -1 cast to size_t on a conversion error.3278*/3279size_t SizeFromStore(3280const char * a_pInputData,3281size_t a_uInputDataLen)3282{3283SI_ASSERT(a_uInputDataLen != (size_t) -1);32843285UErrorCode nError;32863287if (!m_pConverter) {3288nError = U_ZERO_ERROR;3289m_pConverter = ucnv_open(m_pEncoding, &nError);3290if (U_FAILURE(nError)) {3291return (size_t) -1;3292}3293}32943295nError = U_ZERO_ERROR;3296int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,3297a_pInputData, (int32_t) a_uInputDataLen, &nError);3298if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {3299return (size_t) -1;3300}33013302return (size_t) nLen;3303}33043305/** Convert the input string from the storage format to UChar.3306* The storage format is always UTF-8 or MBCS.3307*3308* @param a_pInputData Data in storage format to be converted to UChar.3309* @param a_uInputDataLen Length of storage format data in bytes. This3310* must be the actual length of the data, including3311* NULL byte if NULL terminated string is required.3312* @param a_pOutputData Pointer to the output buffer to received the3313* converted data.3314* @param a_uOutputDataSize Size of the output buffer in UChar.3315* @return true if all of the input data was successfully3316* converted.3317*/3318bool ConvertFromStore(3319const char * a_pInputData,3320size_t a_uInputDataLen,3321UChar * a_pOutputData,3322size_t a_uOutputDataSize)3323{3324UErrorCode nError;33253326if (!m_pConverter) {3327nError = U_ZERO_ERROR;3328m_pConverter = ucnv_open(m_pEncoding, &nError);3329if (U_FAILURE(nError)) {3330return false;3331}3332}33333334nError = U_ZERO_ERROR;3335ucnv_toUChars(m_pConverter,3336a_pOutputData, (int32_t) a_uOutputDataSize,3337a_pInputData, (int32_t) a_uInputDataLen, &nError);3338if (U_FAILURE(nError)) {3339return false;3340}33413342return true;3343}33443345/** Calculate the number of char required by the storage format of this3346* data. The storage format is always UTF-8 or MBCS.3347*3348* @param a_pInputData NULL terminated string to calculate the number of3349* bytes required to be converted to storage format.3350* @return Number of bytes required by the string when3351* converted to storage format. This size always3352* includes space for the terminating NULL character.3353* @return -1 cast to size_t on a conversion error.3354*/3355size_t SizeToStore(3356const UChar * a_pInputData)3357{3358UErrorCode nError;33593360if (!m_pConverter) {3361nError = U_ZERO_ERROR;3362m_pConverter = ucnv_open(m_pEncoding, &nError);3363if (U_FAILURE(nError)) {3364return (size_t) -1;3365}3366}33673368nError = U_ZERO_ERROR;3369int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,3370a_pInputData, -1, &nError);3371if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {3372return (size_t) -1;3373}33743375return (size_t) nLen + 1;3376}33773378/** Convert the input string to the storage format of this data.3379* The storage format is always UTF-8 or MBCS.3380*3381* @param a_pInputData NULL terminated source string to convert. All of3382* the data will be converted including the3383* terminating NULL character.3384* @param a_pOutputData Pointer to the buffer to receive the converted3385* string.3386* @param a_pOutputDataSize Size of the output buffer in char.3387* @return true if all of the input data, including the3388* terminating NULL character was successfully3389* converted.3390*/3391bool ConvertToStore(3392const UChar * a_pInputData,3393char * a_pOutputData,3394size_t a_uOutputDataSize)3395{3396UErrorCode nError;33973398if (!m_pConverter) {3399nError = U_ZERO_ERROR;3400m_pConverter = ucnv_open(m_pEncoding, &nError);3401if (U_FAILURE(nError)) {3402return false;3403}3404}34053406nError = U_ZERO_ERROR;3407ucnv_fromUChars(m_pConverter,3408a_pOutputData, (int32_t) a_uOutputDataSize,3409a_pInputData, -1, &nError);3410if (U_FAILURE(nError)) {3411return false;3412}34133414return true;3415}3416};34173418#endif // SI_CONVERT_ICU341934203421// ---------------------------------------------------------------------------3422// SI_CONVERT_WIN323423// ---------------------------------------------------------------------------3424#ifdef SI_CONVERT_WIN3234253426#define SI_Case SI_GenericCase34273428// Windows CE doesn't have errno or MBCS libraries3429#ifdef _WIN32_WCE3430# ifndef SI_NO_MBCS3431# define SI_NO_MBCS3432# endif3433#endif34343435#include <windows.h>3436#ifdef SI_NO_MBCS3437# define SI_NoCase SI_GenericNoCase3438#else // !SI_NO_MBCS3439/**3440* Case-insensitive comparison class using Win32 MBCS functions. This class3441* returns a case-insensitive semi-collation order for MBCS text. It may not3442* be safe for UTF-8 text returned in char format as we don't know what3443* characters will be folded by the function! Therefore, if you are using3444* SI_CHAR == char and SetUnicode(true), then you need to use the generic3445* SI_NoCase class instead.3446*/3447#include <mbstring.h>3448template<class SI_CHAR>3449struct SI_NoCase {3450bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {3451if (sizeof(SI_CHAR) == sizeof(char)) {3452return _mbsicmp((const unsigned char *)pLeft,3453(const unsigned char *)pRight) < 0;3454}3455if (sizeof(SI_CHAR) == sizeof(wchar_t)) {3456return _wcsicmp((const wchar_t *)pLeft,3457(const wchar_t *)pRight) < 0;3458}3459return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight);3460}3461};3462#endif // SI_NO_MBCS34633464/**3465* Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses3466* only the Win32 functions and doesn't require the external Unicode UTF-83467* conversion library. It will not work on Windows 95 without using Microsoft3468* Layer for Unicode in your application.3469*/3470template<class SI_CHAR>3471class SI_ConvertW {3472UINT m_uCodePage;3473protected:3474SI_ConvertW() { }3475public:3476SI_ConvertW(bool a_bStoreIsUtf8) {3477m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP;3478}34793480/* copy and assignment */3481SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }3482SI_ConvertW & operator=(const SI_ConvertW & rhs) {3483m_uCodePage = rhs.m_uCodePage;3484return *this;3485}34863487/** Calculate the number of SI_CHAR required for converting the input3488* from the storage format. The storage format is always UTF-8 or MBCS.3489*3490* @param a_pInputData Data in storage format to be converted to SI_CHAR.3491* @param a_uInputDataLen Length of storage format data in bytes. This3492* must be the actual length of the data, including3493* NULL byte if NULL terminated string is required.3494* @return Number of SI_CHAR required by the string when3495* converted. If there are embedded NULL bytes in the3496* input data, only the string up and not including3497* the NULL byte will be converted.3498* @return -1 cast to size_t on a conversion error.3499*/3500size_t SizeFromStore(3501const char * a_pInputData,3502size_t a_uInputDataLen)3503{3504SI_ASSERT(a_uInputDataLen != (size_t) -1);35053506int retval = MultiByteToWideChar(3507m_uCodePage, 0,3508a_pInputData, (int) a_uInputDataLen,35090, 0);3510return (size_t)(retval > 0 ? retval : -1);3511}35123513/** Convert the input string from the storage format to SI_CHAR.3514* The storage format is always UTF-8 or MBCS.3515*3516* @param a_pInputData Data in storage format to be converted to SI_CHAR.3517* @param a_uInputDataLen Length of storage format data in bytes. This3518* must be the actual length of the data, including3519* NULL byte if NULL terminated string is required.3520* @param a_pOutputData Pointer to the output buffer to received the3521* converted data.3522* @param a_uOutputDataSize Size of the output buffer in SI_CHAR.3523* @return true if all of the input data was successfully3524* converted.3525*/3526bool ConvertFromStore(3527const char * a_pInputData,3528size_t a_uInputDataLen,3529SI_CHAR * a_pOutputData,3530size_t a_uOutputDataSize)3531{3532int nSize = MultiByteToWideChar(3533m_uCodePage, 0,3534a_pInputData, (int) a_uInputDataLen,3535(wchar_t *) a_pOutputData, (int) a_uOutputDataSize);3536return (nSize > 0);3537}35383539/** Calculate the number of char required by the storage format of this3540* data. The storage format is always UTF-8.3541*3542* @param a_pInputData NULL terminated string to calculate the number of3543* bytes required to be converted to storage format.3544* @return Number of bytes required by the string when3545* converted to storage format. This size always3546* includes space for the terminating NULL character.3547* @return -1 cast to size_t on a conversion error.3548*/3549size_t SizeToStore(3550const SI_CHAR * a_pInputData)3551{3552int retval = WideCharToMultiByte(3553m_uCodePage, 0,3554(const wchar_t *) a_pInputData, -1,35550, 0, 0, 0);3556return (size_t) (retval > 0 ? retval : -1);3557}35583559/** Convert the input string to the storage format of this data.3560* The storage format is always UTF-8 or MBCS.3561*3562* @param a_pInputData NULL terminated source string to convert. All of3563* the data will be converted including the3564* terminating NULL character.3565* @param a_pOutputData Pointer to the buffer to receive the converted3566* string.3567* @param a_pOutputDataSize Size of the output buffer in char.3568* @return true if all of the input data, including the3569* terminating NULL character was successfully3570* converted.3571*/3572bool ConvertToStore(3573const SI_CHAR * a_pInputData,3574char * a_pOutputData,3575size_t a_uOutputDataSize)3576{3577int retval = WideCharToMultiByte(3578m_uCodePage, 0,3579(const wchar_t *) a_pInputData, -1,3580a_pOutputData, (int) a_uOutputDataSize, 0, 0);3581return retval > 0;3582}3583};35843585#endif // SI_CONVERT_WIN323586358735883589// ---------------------------------------------------------------------------3590// SI_NO_CONVERSION3591// ---------------------------------------------------------------------------3592#ifdef SI_NO_CONVERSION35933594#define SI_Case SI_GenericCase3595#define SI_NoCase SI_GenericNoCase35963597#endif // SI_NO_CONVERSION3598359936003601// ---------------------------------------------------------------------------3602// TYPE DEFINITIONS3603// ---------------------------------------------------------------------------36043605typedef CSimpleIniTempl<char,3606SI_NoCase<char>,SI_ConvertA<char> > CSimpleIniA;3607typedef CSimpleIniTempl<char,3608SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA;36093610#if defined(SI_NO_CONVERSION)3611// if there is no wide char conversion then we don't need to define the3612// widechar "W" versions of CSimpleIni3613# define CSimpleIni CSimpleIniA3614# define CSimpleIniCase CSimpleIniCaseA3615# define SI_NEWLINE SI_NEWLINE_A3616#else3617# if defined(SI_CONVERT_ICU)3618typedef CSimpleIniTempl<UChar,3619SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;3620typedef CSimpleIniTempl<UChar,3621SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;3622# else3623typedef CSimpleIniTempl<wchar_t,3624SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;3625typedef CSimpleIniTempl<wchar_t,3626SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;3627# endif36283629# ifdef _UNICODE3630# define CSimpleIni CSimpleIniW3631# define CSimpleIniCase CSimpleIniCaseW3632# define SI_NEWLINE SI_NEWLINE_W3633# else // !_UNICODE3634# define CSimpleIni CSimpleIniA3635# define CSimpleIniCase CSimpleIniCaseA3636# define SI_NEWLINE SI_NEWLINE_A3637# endif // _UNICODE3638#endif36393640#ifdef _MSC_VER3641# pragma warning (pop)3642#endif36433644#endif // INCLUDED_SimpleIni_h3645364636473648