duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

SimpleIni.h (130777B)


      1 /** @mainpage
      2 
      3     <table>
      4         <tr><th>Library     <td>SimpleIni
      5         <tr><th>File        <td>SimpleIni.h
      6         <tr><th>Author      <td>Brodie Thiesfield
      7         <tr><th>Source      <td>https://github.com/brofield/simpleini
      8         <tr><th>Version     <td>4.22
      9     </table>
     10 
     11     Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation.
     12 
     13     @section intro INTRODUCTION
     14 
     15     This component allows an INI-style configuration file to be used on both
     16     Windows and Linux/Unix. It is fast, simple and source code using this
     17     component will compile unchanged on either OS.
     18 
     19     @section features FEATURES
     20 
     21     - MIT Licence allows free use in all software (including GPL and commercial)
     22     - multi-platform (Windows CE/9x/NT..10/etc, Linux, MacOSX, Unix)
     23     - loading and saving of INI-style configuration files
     24     - configuration files can have any newline format on all platforms
     25     - liberal acceptance of file format
     26         - key/values with no section
     27         - removal of whitespace around sections, keys and values
     28     - support for multi-line values (values with embedded newline characters)
     29     - optional support for multiple keys with the same name
     30     - optional case-insensitive sections and keys (for ASCII characters only)
     31     - saves files with sections and keys in the same order as they were loaded
     32     - preserves comments on the file, section and keys where possible.
     33     - supports both char or wchar_t programming interfaces
     34     - supports both MBCS (system locale) and UTF-8 file encodings
     35     - system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file
     36     - support for non-ASCII characters in section, keys, values and comments
     37     - support for non-standard character types or file encodings
     38       via user-written converter classes
     39     - support for adding/modifying values programmatically
     40     - should compile cleanly without warning usually at the strictest warning level
     41     - it has been tested with the following compilers:
     42         - Windows/VC6 (warning level 3)
     43         - Windows/VC.NET 2003 (warning level 4)
     44         - Windows/VC 2005 (warning level 4)
     45         - Windows/VC 2019 (warning level 4)
     46         - Linux/gcc (-Wall)
     47         - Mac OS/c++ (-Wall)
     48 
     49     @section usage USAGE SUMMARY
     50 
     51     -#  Decide if you will be using utf8 or MBCS files, and working with the
     52         data in utf8, wchar_t or ICU chars. 
     53     -#  If you will only be using straight utf8 files and access the data via the 
     54         char interface, then you do not need any conversion library and could define 
     55         SI_NO_CONVERSION. Note that no conversion also means no validation of the data.
     56         If no converter is specified then the default converter is SI_CONVERT_GENERIC 
     57         on Mac/Linux and SI_CONVERT_WIN32 on Windows. If you need widechar support on 
     58         Mac/Linux then use either SI_CONVERT_GENERIC or SI_CONVERT_ICU. These are also
     59         supported on all platforms.
     60     -#  Define the appropriate symbol for the converter you wish to use and
     61         include the SimpleIni.h header file. 
     62     -#  Declare an instance of the appropriate class. Note that the following
     63         definitions are just shortcuts for commonly used types. Other types
     64         (PRUnichar, unsigned short, unsigned char) are also possible.
     65         <table>
     66         <tr><th>Interface       <th>Case-sensitive  <th>Load UTF-8  <th>Load MBCS   <th>Typedef
     67         <tr><th>SI_NO_CONVERSION
     68             <tr><td>char        <td>No              <td>Yes         <td>No          <td>CSimpleIniA
     69             <tr><td>char        <td>Yes             <td>Yes         <td>No          <td>CSimpleIniCaseA
     70         <tr><th>SI_CONVERT_GENERIC
     71             <tr><td>char        <td>No              <td>Yes         <td>Yes #1      <td>CSimpleIniA
     72             <tr><td>char        <td>Yes             <td>Yes         <td>Yes         <td>CSimpleIniCaseA
     73             <tr><td>wchar_t     <td>No              <td>Yes         <td>Yes         <td>CSimpleIniW
     74             <tr><td>wchar_t     <td>Yes             <td>Yes         <td>Yes         <td>CSimpleIniCaseW
     75         <tr><th>SI_CONVERT_WIN32
     76             <tr><td>char        <td>No              <td>No #2       <td>Yes         <td>CSimpleIniA
     77             <tr><td>char        <td>Yes             <td>Yes         <td>Yes         <td>CSimpleIniCaseA
     78             <tr><td>wchar_t     <td>No              <td>Yes         <td>Yes         <td>CSimpleIniW
     79             <tr><td>wchar_t     <td>Yes             <td>Yes         <td>Yes         <td>CSimpleIniCaseW
     80         <tr><th>SI_CONVERT_ICU
     81             <tr><td>char        <td>No              <td>Yes         <td>Yes         <td>CSimpleIniA
     82             <tr><td>char        <td>Yes             <td>Yes         <td>Yes         <td>CSimpleIniCaseA
     83             <tr><td>UChar       <td>No              <td>Yes         <td>Yes         <td>CSimpleIniW
     84             <tr><td>UChar       <td>Yes             <td>Yes         <td>Yes         <td>CSimpleIniCaseW
     85         </table>
     86         #1  On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.<br>
     87         #2  Only affects Windows. On Windows this uses MBCS functions and
     88             so may fold case incorrectly leading to uncertain results.
     89     -# Set all the options that you require, see all the Set*() options below. 
     90         The SetUnicode() option is very common and can be specified in the constructor.
     91     -# Call LoadData() or LoadFile() to load and parse the INI configuration file
     92     -# Access and modify the data of the file using the following functions
     93         <table>
     94             <tr><td>GetAllSections  <td>Return all section names
     95             <tr><td>GetAllKeys      <td>Return all key names within a section
     96             <tr><td>GetAllValues    <td>Return all values within a section & key
     97             <tr><td>GetSection      <td>Return all key names and values in a section
     98             <tr><td>GetSectionSize  <td>Return the number of keys in a section
     99             <tr><td>GetValue        <td>Return a value for a section & key
    100             <tr><td>SetValue        <td>Add or update a value for a section & key
    101             <tr><td>Delete          <td>Remove a section, or a key from a section
    102             <tr><td>SectionExists   <td>Does a section exist?
    103             <tr><td>KeyExists       <td>Does a key exist?
    104         </table>
    105     -# Call Save() or SaveFile() to save the INI configuration data
    106 
    107     @section iostreams IO STREAMS
    108 
    109     SimpleIni supports reading from and writing to STL IO streams. Enable this
    110     by defining SI_SUPPORT_IOSTREAMS before including the SimpleIni.h header
    111     file. Ensure that if the streams are backed by a file (e.g. ifstream or
    112     ofstream) then the flag ios_base::binary has been used when the file was
    113     opened.
    114 
    115     @section multiline MULTI-LINE VALUES
    116 
    117     Values that span multiple lines are created using the following format.
    118 
    119         <pre>
    120         key = <<<ENDTAG
    121         .... multiline value ....
    122         ENDTAG
    123         </pre>
    124 
    125     Note the following:
    126     - The text used for ENDTAG can be anything and is used to find
    127       where the multi-line text ends.
    128     - The newline after ENDTAG in the start tag, and the newline
    129       before ENDTAG in the end tag is not included in the data value.
    130     - The ending tag must be on it's own line with no whitespace before
    131       or after it.
    132     - The multi-line value is modified at load so that each line in the value
    133       is delimited by a single '\\n' character on all platforms. At save time
    134       it will be converted into the newline format used by the current
    135       platform.
    136 
    137     @section comments COMMENTS
    138 
    139     Comments are preserved in the file within the following restrictions:
    140     - Every file may have a single "file comment". It must start with the
    141       first character in the file, and will end with the first non-comment
    142       line in the file.
    143     - Every section may have a single "section comment". It will start
    144       with the first comment line following the file comment, or the last
    145       data entry. It ends at the beginning of the section.
    146     - Every key may have a single "key comment". This comment will start
    147       with the first comment line following the section start, or the file
    148       comment if there is no section name.
    149     - Comments are set at the time that the file, section or key is first
    150       created. The only way to modify a comment on a section or a key is to
    151       delete that entry and recreate it with the new comment. There is no
    152       way to change the file comment.
    153 
    154     @section save SAVE ORDER
    155 
    156     The sections and keys are written out in the same order as they were
    157     read in from the file. Sections and keys added to the data after the
    158     file has been loaded will be added to the end of the file when it is
    159     written. There is no way to specify the location of a section or key
    160     other than in first-created, first-saved order.
    161 
    162     @section notes NOTES
    163 
    164     - To load UTF-8 data on Windows 95, you need to use Microsoft Layer for
    165       Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU.
    166     - When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked.
    167     - When using SI_CONVERT_ICU, ICU header files must be on the include
    168       path and icuuc.lib must be linked in.
    169     - To load a UTF-8 file on Windows AND expose it with SI_CHAR == char,
    170       you should use SI_CONVERT_GENERIC.
    171     - The collation (sorting) order used for sections and keys returned from
    172       iterators is NOT DEFINED. If collation order of the text is important
    173       then it should be done yourself by either supplying a replacement
    174       SI_STRLESS class, or by sorting the strings external to this library.
    175     - Usage of the <mbstring.h> header on Windows can be disabled by defining
    176       SI_NO_MBCS. This is defined automatically on Windows CE platforms.
    177     - Not thread-safe so manage your own locking
    178 
    179     @section contrib CONTRIBUTIONS
    180 
    181     Many thanks to the following contributors:
    182     
    183     - 2010/05/03: Tobias Gehrig: added GetDoubleValue()
    184     - See list of many contributors in github
    185 
    186     @section licence MIT LICENCE
    187 
    188     The licence text below is the boilerplate "MIT Licence" used from:
    189     http://www.opensource.org/licenses/mit-license.php
    190 
    191     Copyright (c) 2006-2024, Brodie Thiesfield
    192 
    193     Permission is hereby granted, free of charge, to any person obtaining a copy
    194     of this software and associated documentation files (the "Software"), to deal
    195     in the Software without restriction, including without limitation the rights
    196     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    197     copies of the Software, and to permit persons to whom the Software is furnished
    198     to do so, subject to the following conditions:
    199 
    200     The above copyright notice and this permission notice shall be included in
    201     all copies or substantial portions of the Software.
    202 
    203     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    204     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    205     FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    206     COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    207     IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    208     CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    209 */
    210 
    211 #ifndef INCLUDED_SimpleIni_h
    212 #define INCLUDED_SimpleIni_h
    213 
    214 #if defined(_MSC_VER) && (_MSC_VER >= 1020)
    215 # pragma once
    216 #endif
    217 
    218 // Disable these warnings in MSVC:
    219 //  4127 "conditional expression is constant" as the conversion classes trigger
    220 //  it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
    221 //  be optimized away in a release build.
    222 //  4503 'insert' : decorated name length exceeded, name was truncated
    223 //  4702 "unreachable code" as the MS STL header causes it in release mode.
    224 //  Again, the code causing the warning will be cleaned up by the compiler.
    225 //  4786 "identifier truncated to 256 characters" as this is thrown hundreds
    226 //  of times VC6 as soon as STL is used.
    227 #ifdef _MSC_VER
    228 # pragma warning (push)
    229 # pragma warning (disable: 4127 4503 4702 4786)
    230 #endif
    231 
    232 #include <cstring>
    233 #include <cstdlib>
    234 #include <string>
    235 #include <map>
    236 #include <list>
    237 #include <algorithm>
    238 #include <stdio.h>
    239 
    240 #ifdef SI_SUPPORT_IOSTREAMS
    241 # include <iostream>
    242 #endif // SI_SUPPORT_IOSTREAMS
    243 
    244 #ifdef _DEBUG
    245 # ifndef assert
    246 #  include <cassert>
    247 # endif
    248 # define SI_ASSERT(x)   assert(x)
    249 #else
    250 # define SI_ASSERT(x)
    251 #endif
    252 
    253 using SI_Error = int;
    254 
    255 constexpr int SI_OK = 0;        //!< No error
    256 constexpr int SI_UPDATED = 1;   //!< An existing value was updated
    257 constexpr int SI_INSERTED = 2;  //!< A new value was inserted
    258 
    259 // note: test for any error with (retval < 0)
    260 constexpr int SI_FAIL = -1;     //!< Generic failure
    261 constexpr int SI_NOMEM = -2;    //!< Out of memory error
    262 constexpr int SI_FILE = -3;     //!< File error (see errno for detail error)
    263 
    264 #define SI_UTF8_SIGNATURE     "\xEF\xBB\xBF"
    265 
    266 #ifdef _WIN32
    267 # define SI_NEWLINE_A   "\r\n"
    268 # define SI_NEWLINE_W   L"\r\n"
    269 #else // !_WIN32
    270 # define SI_NEWLINE_A   "\n"
    271 # define SI_NEWLINE_W   L"\n"
    272 #endif // _WIN32
    273 
    274 #if defined(SI_CONVERT_ICU)
    275 # include <unicode/ustring.h>
    276 #endif
    277 
    278 #if defined(_WIN32)
    279 # define SI_HAS_WIDE_FILE
    280 # define SI_WCHAR_T     wchar_t
    281 #elif defined(SI_CONVERT_ICU)
    282 # define SI_HAS_WIDE_FILE
    283 # define SI_WCHAR_T     UChar
    284 #endif
    285 
    286 
    287 // ---------------------------------------------------------------------------
    288 //                              MAIN TEMPLATE CLASS
    289 // ---------------------------------------------------------------------------
    290 
    291 /** Simple INI file reader.
    292 
    293     This can be instantiated with the choice of unicode or native characterset,
    294     and case sensitive or insensitive comparisons of section and key names.
    295     The supported combinations are pre-defined with the following typedefs:
    296 
    297     <table>
    298         <tr><th>Interface   <th>Case-sensitive  <th>Typedef
    299         <tr><td>char        <td>No              <td>CSimpleIniA
    300         <tr><td>char        <td>Yes             <td>CSimpleIniCaseA
    301         <tr><td>wchar_t     <td>No              <td>CSimpleIniW
    302         <tr><td>wchar_t     <td>Yes             <td>CSimpleIniCaseW
    303     </table>
    304 
    305     Note that using other types for the SI_CHAR is supported. For instance,
    306     unsigned char, unsigned short, etc. Note that where the alternative type
    307     is a different size to char/wchar_t you may need to supply new helper
    308     classes for SI_STRLESS and SI_CONVERTER.
    309  */
    310 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
    311 class CSimpleIniTempl
    312 {
    313 public:
    314     typedef SI_CHAR SI_CHAR_T;
    315 
    316     /** key entry */
    317     struct Entry {
    318         const SI_CHAR * pItem;
    319         const SI_CHAR * pComment;
    320         int             nOrder;
    321 
    322         Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0)
    323             : pItem(a_pszItem)
    324             , pComment(NULL)
    325             , nOrder(a_nOrder)
    326         { }
    327         Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder)
    328             : pItem(a_pszItem)
    329             , pComment(a_pszComment)
    330             , nOrder(a_nOrder)
    331         { }
    332         Entry(const Entry & rhs) { operator=(rhs); }
    333         Entry & operator=(const Entry & rhs) {
    334             pItem    = rhs.pItem;
    335             pComment = rhs.pComment;
    336             nOrder   = rhs.nOrder;
    337             return *this;
    338         }
    339 
    340 #if defined(_MSC_VER) && _MSC_VER <= 1200
    341         /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */
    342         bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); }
    343         bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); }
    344 #endif
    345 
    346         /** Strict less ordering by name of key only */
    347         struct KeyOrder {
    348             bool operator()(const Entry & lhs, const Entry & rhs) const {
    349                 const static SI_STRLESS isLess = SI_STRLESS();
    350                 return isLess(lhs.pItem, rhs.pItem);
    351             }
    352         };
    353 
    354         /** Strict less ordering by order, and then name of key */
    355         struct LoadOrder {
    356             bool operator()(const Entry & lhs, const Entry & rhs) const {
    357                 if (lhs.nOrder != rhs.nOrder) {
    358                     return lhs.nOrder < rhs.nOrder;
    359                 }
    360                 return KeyOrder()(lhs.pItem, rhs.pItem);
    361             }
    362         };
    363     };
    364 
    365     /** map keys to values */
    366     typedef std::multimap<Entry,const SI_CHAR *,typename Entry::KeyOrder> TKeyVal;
    367 
    368     /** map sections to key/value map */
    369     typedef std::map<Entry,TKeyVal,typename Entry::KeyOrder> TSection;
    370 
    371     /** set of dependent string pointers. Note that these pointers are
    372         dependent on memory owned by CSimpleIni.
    373     */
    374     typedef std::list<Entry> TNamesDepend;
    375 
    376     /** interface definition for the OutputWriter object to pass to Save()
    377         in order to output the INI file data.
    378     */
    379     class OutputWriter {
    380     public:
    381         OutputWriter() { }
    382         virtual ~OutputWriter() { }
    383         virtual void Write(const char * a_pBuf) = 0;
    384     private:
    385         OutputWriter(const OutputWriter &);             // disable
    386         OutputWriter & operator=(const OutputWriter &); // disable
    387     };
    388 
    389     /** OutputWriter class to write the INI data to a file */
    390     class FileWriter : public OutputWriter {
    391         FILE * m_file;
    392     public:
    393         FileWriter(FILE * a_file) : m_file(a_file) { }
    394         void Write(const char * a_pBuf) {
    395             fputs(a_pBuf, m_file);
    396         }
    397     private:
    398         FileWriter(const FileWriter &);             // disable
    399         FileWriter & operator=(const FileWriter &); // disable
    400     };
    401 
    402     /** OutputWriter class to write the INI data to a string */
    403     class StringWriter : public OutputWriter {
    404         std::string & m_string;
    405     public:
    406         StringWriter(std::string & a_string) : m_string(a_string) { }
    407         void Write(const char * a_pBuf) {
    408             m_string.append(a_pBuf);
    409         }
    410     private:
    411         StringWriter(const StringWriter &);             // disable
    412         StringWriter & operator=(const StringWriter &); // disable
    413     };
    414 
    415 #ifdef SI_SUPPORT_IOSTREAMS
    416     /** OutputWriter class to write the INI data to an ostream */
    417     class StreamWriter : public OutputWriter {
    418         std::ostream & m_ostream;
    419     public:
    420         StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { }
    421         void Write(const char * a_pBuf) {
    422             m_ostream << a_pBuf;
    423         }
    424     private:
    425         StreamWriter(const StreamWriter &);             // disable
    426         StreamWriter & operator=(const StreamWriter &); // disable
    427     };
    428 #endif // SI_SUPPORT_IOSTREAMS
    429 
    430     /** Characterset conversion utility class to convert strings to the
    431         same format as is used for the storage.
    432     */
    433     class Converter : private SI_CONVERTER {
    434     public:
    435         Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) {
    436             m_scratch.resize(1024);
    437         }
    438         Converter(const Converter & rhs) { operator=(rhs); }
    439         Converter & operator=(const Converter & rhs) {
    440             m_scratch = rhs.m_scratch;
    441             return *this;
    442         }
    443         bool ConvertToStore(const SI_CHAR * a_pszString) {
    444             size_t uLen = SI_CONVERTER::SizeToStore(a_pszString);
    445             if (uLen == (size_t)(-1)) {
    446                 return false;
    447             }
    448             while (uLen > m_scratch.size()) {
    449                 m_scratch.resize(m_scratch.size() * 2);
    450             }
    451             return SI_CONVERTER::ConvertToStore(
    452                 a_pszString,
    453                 const_cast<char*>(m_scratch.data()),
    454                 m_scratch.size());
    455         }
    456         const char * Data() { return m_scratch.data(); }
    457     private:
    458         std::string m_scratch;
    459     };
    460 
    461 public:
    462     /*-----------------------------------------------------------------------*/
    463 
    464     /** Default constructor.
    465 
    466         @param a_bIsUtf8     See the method SetUnicode() for details.
    467         @param a_bMultiKey   See the method SetMultiKey() for details.
    468         @param a_bMultiLine  See the method SetMultiLine() for details.
    469      */
    470     CSimpleIniTempl(
    471         bool a_bIsUtf8    = false,
    472         bool a_bMultiKey  = false,
    473         bool a_bMultiLine = false
    474         );
    475 
    476     /** Destructor */
    477     ~CSimpleIniTempl();
    478 
    479     /** Deallocate all memory stored by this object */
    480     void Reset();
    481 
    482     /** Has any data been loaded */
    483     bool IsEmpty() const { return m_data.empty(); }
    484 
    485     /*-----------------------------------------------------------------------*/
    486     /** @{ @name Settings */
    487 
    488     /** Set the storage format of the INI data. This affects both the loading
    489         and saving of the INI data using all of the Load/Save API functions.
    490         This value cannot be changed after any INI data has been loaded.
    491 
    492         If the file is not set to Unicode (UTF-8), then the data encoding is
    493         assumed to be the OS native encoding. This encoding is the system
    494         locale on Linux/Unix and the legacy MBCS encoding on Windows NT/2K/XP.
    495         If the storage format is set to Unicode then the file will be loaded
    496         as UTF-8 encoded data regardless of the native file encoding. If
    497         SI_CHAR == char then all of the char* parameters take and return UTF-8
    498         encoded data regardless of the system locale.
    499 
    500         \param a_bIsUtf8     Assume UTF-8 encoding for the source?
    501      */
    502     void SetUnicode(bool a_bIsUtf8 = true) {
    503         if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8;
    504     }
    505 
    506     /** Get the storage format of the INI data. */
    507     bool IsUnicode() const { return m_bStoreIsUtf8; }
    508 
    509     /** Should multiple identical keys be permitted in the file. If set to false
    510         then the last value encountered will be used as the value of the key.
    511         If set to true, then all values will be available to be queried. For
    512         example, with the following input:
    513 
    514         <pre>
    515         [section]
    516         test=value1
    517         test=value2
    518         </pre>
    519 
    520         Then with SetMultiKey(true), both of the values "value1" and "value2"
    521         will be returned for the key test. If SetMultiKey(false) is used, then
    522         the value for "test" will only be "value2". This value may be changed
    523         at any time.
    524 
    525         \param a_bAllowMultiKey  Allow multi-keys in the source?
    526      */
    527     void SetMultiKey(bool a_bAllowMultiKey = true) {
    528         m_bAllowMultiKey = a_bAllowMultiKey;
    529     }
    530 
    531     /** Get the storage format of the INI data. */
    532     bool IsMultiKey() const { return m_bAllowMultiKey; }
    533 
    534     /** Should data values be permitted to span multiple lines in the file. If
    535         set to false then the multi-line construct <<<TAG as a value will be
    536         returned as is instead of loading the data. This value may be changed
    537         at any time.
    538 
    539         \param a_bAllowMultiLine     Allow multi-line values in the source?
    540      */
    541     void SetMultiLine(bool a_bAllowMultiLine = true) {
    542         m_bAllowMultiLine = a_bAllowMultiLine;
    543     }
    544 
    545     /** Query the status of multi-line data */
    546     bool IsMultiLine() const { return m_bAllowMultiLine; }
    547 
    548     /** Should spaces be added around the equals sign when writing key/value
    549         pairs out. When true, the result will be "key = value". When false, 
    550         the result will be "key=value". This value may be changed at any time.
    551 
    552         \param a_bSpaces     Add spaces around the equals sign?
    553      */
    554     void SetSpaces(bool a_bSpaces = true) {
    555         m_bSpaces = a_bSpaces;
    556     }
    557 
    558     /** Query the status of spaces output */
    559     bool UsingSpaces() const { return m_bSpaces; }
    560     
    561 
    562     /** Should we recognise and parse quotes in single line values?
    563 
    564         \param a_bParseQuotes  Parse quoted data in values?
    565      */
    566     void SetQuotes(bool a_bParseQuotes = true) {
    567         m_bParseQuotes = a_bParseQuotes;
    568     }
    569 
    570     /** Are we permitting keys and values to be quoted? */
    571     bool UsingQuotes() const { return m_bParseQuotes; }
    572 
    573     /** When reading/writing an ini file, do we require every key to have an equals
    574         sign to delineate a valid key value. If false, then every valid key must
    575         have an equals sign and any lines without an equals sign is ignored. If
    576         true then keys do not require an equals sign to be considered a key. Note 
    577         that this means that any non-commented line of text would become a key.
    578 
    579         \param a_bAllowKeyOnly  Permit keys without an equals sign or value.
    580      */
    581     void SetAllowKeyOnly(bool a_bAllowKeyOnly = true) {
    582         m_bAllowKeyOnly = a_bAllowKeyOnly;
    583     }
    584 
    585     /** Do we allow keys to exist without a value or equals sign? */
    586     bool GetAllowKeyOnly() const { return m_bAllowKeyOnly; }
    587 
    588 
    589 
    590     /*-----------------------------------------------------------------------*/
    591     /** @}
    592         @{ @name Loading INI Data */
    593 
    594     /** Load an INI file from disk into memory
    595 
    596         @param a_pszFile    Path of the file to be loaded. This will be passed
    597                             to fopen() and so must be a valid path for the
    598                             current platform.
    599 
    600         @return SI_Error    See error definitions
    601      */
    602     SI_Error LoadFile(
    603         const char * a_pszFile
    604         );
    605 
    606 #ifdef SI_HAS_WIDE_FILE
    607     /** Load an INI file from disk into memory
    608 
    609         @param a_pwszFile   Path of the file to be loaded in UTF-16.
    610 
    611         @return SI_Error    See error definitions
    612      */
    613     SI_Error LoadFile(
    614         const SI_WCHAR_T * a_pwszFile
    615         );
    616 #endif // SI_HAS_WIDE_FILE
    617 
    618     /** Load the file from a file pointer.
    619 
    620         @param a_fpFile     Valid file pointer to read the file data from. The
    621                             file will be read until end of file.
    622 
    623         @return SI_Error    See error definitions
    624     */
    625     SI_Error LoadFile(
    626         FILE * a_fpFile
    627         );
    628 
    629 #ifdef SI_SUPPORT_IOSTREAMS
    630     /** Load INI file data from an istream.
    631 
    632         @param a_istream    Stream to read from
    633 
    634         @return SI_Error    See error definitions
    635      */
    636     SI_Error LoadData(
    637         std::istream & a_istream
    638         );
    639 #endif // SI_SUPPORT_IOSTREAMS
    640 
    641     /** Load INI file data direct from a std::string
    642 
    643         @param a_strData    Data to be loaded
    644 
    645         @return SI_Error    See error definitions
    646      */
    647     SI_Error LoadData(const std::string & a_strData) {
    648         return LoadData(a_strData.c_str(), a_strData.size());
    649     }
    650 
    651     /** Load INI file data direct from memory
    652 
    653         @param a_pData      Data to be loaded
    654         @param a_uDataLen   Length of the data in bytes
    655 
    656         @return SI_Error    See error definitions
    657      */
    658     SI_Error LoadData(
    659         const char *    a_pData,
    660         size_t          a_uDataLen
    661         );
    662 
    663     /*-----------------------------------------------------------------------*/
    664     /** @}
    665         @{ @name Saving INI Data */
    666 
    667     /** Save an INI file from memory to disk
    668 
    669         @param a_pszFile    Path of the file to be saved. This will be passed
    670                             to fopen() and so must be a valid path for the
    671                             current platform.
    672 
    673         @param a_bAddSignature  Prepend the UTF-8 BOM if the output data is
    674                             in UTF-8 format. If it is not UTF-8 then
    675                             this parameter is ignored.
    676 
    677         @return SI_Error    See error definitions
    678      */
    679     SI_Error SaveFile(
    680         const char *    a_pszFile,
    681         bool            a_bAddSignature = true
    682         ) const;
    683 
    684 #ifdef SI_HAS_WIDE_FILE
    685     /** Save an INI file from memory to disk
    686 
    687         @param a_pwszFile   Path of the file to be saved in UTF-16.
    688 
    689         @param a_bAddSignature  Prepend the UTF-8 BOM if the output data is
    690                             in UTF-8 format. If it is not UTF-8 then
    691                             this parameter is ignored.
    692 
    693         @return SI_Error    See error definitions
    694      */
    695     SI_Error SaveFile(
    696         const SI_WCHAR_T *  a_pwszFile,
    697         bool                a_bAddSignature = true
    698         ) const;
    699 #endif // _WIN32
    700 
    701     /** Save the INI data to a file. See Save() for details.
    702 
    703         @param a_pFile      Handle to a file. File should be opened for
    704                             binary output.
    705 
    706         @param a_bAddSignature  Prepend the UTF-8 BOM if the output data is in
    707                             UTF-8 format. If it is not UTF-8 then this value is
    708                             ignored. Do not set this to true if anything has
    709                             already been written to the file.
    710 
    711         @return SI_Error    See error definitions
    712      */
    713     SI_Error SaveFile(
    714         FILE *  a_pFile,
    715         bool    a_bAddSignature = false
    716         ) const;
    717 
    718     /** Save the INI data. The data will be written to the output device
    719         in a format appropriate to the current data, selected by:
    720 
    721         <table>
    722             <tr><th>SI_CHAR     <th>FORMAT
    723             <tr><td>char        <td>same format as when loaded (MBCS or UTF-8)
    724             <tr><td>wchar_t     <td>UTF-8
    725             <tr><td>other       <td>UTF-8
    726         </table>
    727 
    728         Note that comments from the original data is preserved as per the
    729         documentation on comments. The order of the sections and values
    730         from the original file will be preserved.
    731 
    732         Any data prepended or appended to the output device must use the the
    733         same format (MBCS or UTF-8). You may use the GetConverter() method to
    734         convert text to the correct format regardless of the output format
    735         being used by SimpleIni.
    736 
    737         To add a BOM to UTF-8 data, write it out manually at the very beginning
    738         like is done in SaveFile when a_bUseBOM is true.
    739 
    740         @param a_oOutput    Output writer to write the data to.
    741 
    742         @param a_bAddSignature  Prepend the UTF-8 BOM if the output data is in
    743                             UTF-8 format. If it is not UTF-8 then this value is
    744                             ignored. Do not set this to true if anything has
    745                             already been written to the OutputWriter.
    746 
    747         @return SI_Error    See error definitions
    748      */
    749     SI_Error Save(
    750         OutputWriter &  a_oOutput,
    751         bool            a_bAddSignature = false
    752         ) const;
    753 
    754 #ifdef SI_SUPPORT_IOSTREAMS
    755     /** Save the INI data to an ostream. See Save() for details.
    756 
    757         @param a_ostream    String to have the INI data appended to.
    758 
    759         @param a_bAddSignature  Prepend the UTF-8 BOM if the output data is in
    760                             UTF-8 format. If it is not UTF-8 then this value is
    761                             ignored. Do not set this to true if anything has
    762                             already been written to the stream.
    763 
    764         @return SI_Error    See error definitions
    765      */
    766     SI_Error Save(
    767         std::ostream &  a_ostream,
    768         bool            a_bAddSignature = false
    769         ) const
    770     {
    771         StreamWriter writer(a_ostream);
    772         return Save(writer, a_bAddSignature);
    773     }
    774 #endif // SI_SUPPORT_IOSTREAMS
    775 
    776     /** Append the INI data to a string. See Save() for details.
    777 
    778         @param a_sBuffer    String to have the INI data appended to.
    779 
    780         @param a_bAddSignature  Prepend the UTF-8 BOM if the output data is in
    781                             UTF-8 format. If it is not UTF-8 then this value is
    782                             ignored. Do not set this to true if anything has
    783                             already been written to the string.
    784 
    785         @return SI_Error    See error definitions
    786      */
    787     SI_Error Save(
    788         std::string &   a_sBuffer,
    789         bool            a_bAddSignature = false
    790         ) const
    791     {
    792         StringWriter writer(a_sBuffer);
    793         return Save(writer, a_bAddSignature);
    794     }
    795 
    796     /*-----------------------------------------------------------------------*/
    797     /** @}
    798         @{ @name Accessing INI Data */
    799 
    800     /** Retrieve the number keys across all sections.
    801         @return number of keys currently present.
    802      */
    803     size_t GetKeyCount() const;
    804 
    805     /** Retrieve all section names. The list is returned as an STL vector of
    806         names and can be iterated or searched as necessary. Note that the
    807         sort order of the returned strings is NOT DEFINED. You can sort
    808         the names into the load order if desired. Search this file for ".sort"
    809         for an example.
    810 
    811         NOTE! This structure contains only pointers to strings. The actual
    812         string data is stored in memory owned by CSimpleIni. Ensure that the
    813         CSimpleIni object is not destroyed or Reset() while these pointers
    814         are in use!
    815 
    816         @param a_names          Vector that will receive all of the section
    817                                  names. See note above!
    818      */
    819     void GetAllSections(
    820         TNamesDepend & a_names
    821         ) const;
    822 
    823     /** Retrieve all unique key names in a section. The sort order of the
    824         returned strings is NOT DEFINED. You can sort the names into the load 
    825         order if desired. Search this file for ".sort" for an example. Only 
    826         unique key names are returned.
    827 
    828         NOTE! This structure contains only pointers to strings. The actual
    829         string data is stored in memory owned by CSimpleIni. Ensure that the
    830         CSimpleIni object is not destroyed or Reset() while these strings
    831         are in use!
    832 
    833         @param a_pSection       Section to request data for
    834         @param a_names          List that will receive all of the key
    835                                  names. See note above!
    836 
    837         @return true            Section was found.
    838         @return false           Matching section was not found.
    839      */
    840     bool GetAllKeys(
    841         const SI_CHAR * a_pSection,
    842         TNamesDepend &  a_names
    843         ) const;
    844 
    845     /** Retrieve all values for a specific key. This method can be used when
    846         multiple keys are both enabled and disabled. Note that the sort order 
    847         of the returned strings is NOT DEFINED. You can sort the names into 
    848         the load order if desired. Search this file for ".sort" for an example.
    849 
    850         NOTE! The returned values are pointers to string data stored in memory
    851         owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
    852         or Reset while you are using this pointer!
    853 
    854         @param a_pSection       Section to search
    855         @param a_pKey           Key to search for
    856         @param a_values         List to return if the key is not found
    857 
    858         @return true            Key was found.
    859         @return false           Matching section/key was not found.
    860      */
    861     bool GetAllValues(
    862         const SI_CHAR * a_pSection,
    863         const SI_CHAR * a_pKey,
    864         TNamesDepend &  a_values
    865         ) const;
    866 
    867     /** Query the number of keys in a specific section. Note that if multiple
    868         keys are enabled, then this value may be different to the number of
    869         keys returned by GetAllKeys.
    870 
    871         @param a_pSection       Section to request data for
    872 
    873         @return -1              Section does not exist in the file
    874         @return >=0             Number of keys in the section
    875      */
    876     int GetSectionSize(
    877         const SI_CHAR * a_pSection
    878         ) const;
    879 
    880     /** Retrieve all key and value pairs for a section. The data is returned
    881         as a pointer to an STL map and can be iterated or searched as
    882         desired. Note that multiple entries for the same key may exist when
    883         multiple keys have been enabled.
    884 
    885         NOTE! This structure contains only pointers to strings. The actual
    886         string data is stored in memory owned by CSimpleIni. Ensure that the
    887         CSimpleIni object is not destroyed or Reset() while these strings
    888         are in use!
    889 
    890         @param a_pSection       Name of the section to return
    891         @return                 Section data
    892      */
    893     const TKeyVal * GetSection(
    894         const SI_CHAR * a_pSection
    895         ) const;
    896 
    897     /** Test if a section exists. Convenience function */
    898     inline bool SectionExists(
    899         const SI_CHAR * a_pSection
    900     ) const {
    901         return GetSection(a_pSection) != NULL;
    902     }
    903 
    904     /** Test if the key exists in a section. Convenience function. */
    905     inline bool KeyExists(
    906         const SI_CHAR * a_pSection,
    907         const SI_CHAR * a_pKey
    908     ) const {
    909         return GetValue(a_pSection, a_pKey) != NULL;
    910     }
    911 
    912     /** Retrieve the value for a specific key. If multiple keys are enabled
    913         (see SetMultiKey) then only the first value associated with that key
    914         will be returned, see GetAllValues for getting all values with multikey.
    915 
    916         NOTE! The returned value is a pointer to string data stored in memory
    917         owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
    918         or Reset while you are using this pointer!
    919 
    920         @param a_pSection       Section to search
    921         @param a_pKey           Key to search for
    922         @param a_pDefault       Value to return if the key is not found
    923         @param a_pHasMultiple   Optionally receive notification of if there are
    924                                 multiple entries for this key.
    925 
    926         @return a_pDefault      Key was not found in the section
    927         @return other           Value of the key
    928      */
    929     const SI_CHAR * GetValue(
    930         const SI_CHAR * a_pSection,
    931         const SI_CHAR * a_pKey,
    932         const SI_CHAR * a_pDefault     = NULL,
    933         bool *          a_pHasMultiple = NULL
    934         ) const;
    935 
    936     /** Retrieve a numeric value for a specific key. If multiple keys are enabled
    937         (see SetMultiKey) then only the first value associated with that key
    938         will be returned, see GetAllValues for getting all values with multikey.
    939 
    940         @param a_pSection       Section to search
    941         @param a_pKey           Key to search for
    942         @param a_nDefault       Value to return if the key is not found
    943         @param a_pHasMultiple   Optionally receive notification of if there are
    944                                 multiple entries for this key.
    945 
    946         @return a_nDefault      Key was not found in the section
    947         @return other           Value of the key
    948      */
    949     long GetLongValue(
    950         const SI_CHAR * a_pSection,
    951         const SI_CHAR * a_pKey,
    952         long            a_nDefault     = 0,
    953         bool *          a_pHasMultiple = NULL
    954         ) const;
    955 
    956     /** Retrieve a numeric value for a specific key. If multiple keys are enabled
    957         (see SetMultiKey) then only the first value associated with that key
    958         will be returned, see GetAllValues for getting all values with multikey.
    959 
    960         @param a_pSection       Section to search
    961         @param a_pKey           Key to search for
    962         @param a_nDefault       Value to return if the key is not found
    963         @param a_pHasMultiple   Optionally receive notification of if there are
    964                                 multiple entries for this key.
    965 
    966         @return a_nDefault      Key was not found in the section
    967         @return other           Value of the key
    968      */
    969     double GetDoubleValue(
    970         const SI_CHAR * a_pSection,
    971         const SI_CHAR * a_pKey,
    972         double          a_nDefault     = 0,
    973         bool *          a_pHasMultiple = NULL
    974         ) const;
    975 
    976     /** Retrieve a boolean value for a specific key. If multiple keys are enabled
    977         (see SetMultiKey) then only the first value associated with that key
    978         will be returned, see GetAllValues for getting all values with multikey.
    979 
    980         Strings starting with "t", "y", "on" or "1" are returned as logically true.
    981         Strings starting with "f", "n", "of" or "0" are returned as logically false.
    982         For all other values the default is returned. Character comparisons are 
    983         case-insensitive.
    984 
    985         @param a_pSection       Section to search
    986         @param a_pKey           Key to search for
    987         @param a_bDefault       Value to return if the key is not found
    988         @param a_pHasMultiple   Optionally receive notification of if there are
    989                                 multiple entries for this key.
    990 
    991         @return a_nDefault      Key was not found in the section
    992         @return other           Value of the key
    993      */
    994     bool GetBoolValue(
    995         const SI_CHAR * a_pSection,
    996         const SI_CHAR * a_pKey,
    997         bool            a_bDefault     = false,
    998         bool *          a_pHasMultiple = NULL
    999         ) const;
   1000 
   1001     /** Add or update a section or value. This will always insert
   1002         when multiple keys are enabled.
   1003 
   1004         @param a_pSection   Section to add or update
   1005         @param a_pKey       Key to add or update. Set to NULL to
   1006                             create an empty section.
   1007         @param a_pValue     Value to set. Set to NULL to create an
   1008                             empty section.
   1009         @param a_pComment   Comment to be associated with the section or the
   1010                             key. If a_pKey is NULL then it will be associated
   1011                             with the section, otherwise the key. Note that a
   1012                             comment may be set ONLY when the section or key is
   1013                             first created (i.e. when this function returns the
   1014                             value SI_INSERTED). If you wish to create a section
   1015                             with a comment then you need to create the section
   1016                             separately to the key. The comment string must be
   1017                             in full comment form already (have a comment
   1018                             character starting every line).
   1019         @param a_bForceReplace  Should all existing values in a multi-key INI
   1020                             file be replaced with this entry. This option has
   1021                             no effect if not using multi-key files. The 
   1022                             difference between Delete/SetValue and SetValue
   1023                             with a_bForceReplace = true, is that the load 
   1024                             order and comment will be preserved this way.
   1025 
   1026         @return SI_Error    See error definitions
   1027         @return SI_UPDATED  Value was updated
   1028         @return SI_INSERTED Value was inserted
   1029      */
   1030     SI_Error SetValue(
   1031         const SI_CHAR * a_pSection,
   1032         const SI_CHAR * a_pKey,
   1033         const SI_CHAR * a_pValue,
   1034         const SI_CHAR * a_pComment      = NULL,
   1035         bool            a_bForceReplace = false
   1036         )
   1037     {
   1038         return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true);
   1039     }
   1040 
   1041     /** Add or update a numeric value. This will always insert
   1042         when multiple keys are enabled.
   1043 
   1044         @param a_pSection   Section to add or update
   1045         @param a_pKey       Key to add or update. 
   1046         @param a_nValue     Value to set. 
   1047         @param a_pComment   Comment to be associated with the key. See the 
   1048                             notes on SetValue() for comments.
   1049         @param a_bUseHex    By default the value will be written to the file 
   1050                             in decimal format. Set this to true to write it 
   1051                             as hexadecimal.
   1052         @param a_bForceReplace  Should all existing values in a multi-key INI
   1053                             file be replaced with this entry. This option has
   1054                             no effect if not using multi-key files. The 
   1055                             difference between Delete/SetLongValue and 
   1056                             SetLongValue with a_bForceReplace = true, is that 
   1057                             the load order and comment will be preserved this 
   1058                             way.
   1059 
   1060         @return SI_Error    See error definitions
   1061         @return SI_UPDATED  Value was updated
   1062         @return SI_INSERTED Value was inserted
   1063      */
   1064     SI_Error SetLongValue(
   1065         const SI_CHAR * a_pSection,
   1066         const SI_CHAR * a_pKey,
   1067         long            a_nValue,
   1068         const SI_CHAR * a_pComment      = NULL,
   1069         bool            a_bUseHex       = false,
   1070         bool            a_bForceReplace = false
   1071         );
   1072 
   1073     /** Add or update a double value. This will always insert
   1074         when multiple keys are enabled.
   1075 
   1076         @param a_pSection   Section to add or update
   1077         @param a_pKey       Key to add or update. 
   1078         @param a_nValue     Value to set. 
   1079         @param a_pComment   Comment to be associated with the key. See the 
   1080                             notes on SetValue() for comments.
   1081         @param a_bForceReplace  Should all existing values in a multi-key INI
   1082                             file be replaced with this entry. This option has
   1083                             no effect if not using multi-key files. The 
   1084                             difference between Delete/SetDoubleValue and 
   1085                             SetDoubleValue with a_bForceReplace = true, is that 
   1086                             the load order and comment will be preserved this 
   1087                             way.
   1088 
   1089         @return SI_Error    See error definitions
   1090         @return SI_UPDATED  Value was updated
   1091         @return SI_INSERTED Value was inserted
   1092      */
   1093     SI_Error SetDoubleValue(
   1094         const SI_CHAR * a_pSection,
   1095         const SI_CHAR * a_pKey,
   1096         double          a_nValue,
   1097         const SI_CHAR * a_pComment      = NULL,
   1098         bool            a_bForceReplace = false
   1099         );
   1100 
   1101     /** Add or update a boolean value. This will always insert
   1102         when multiple keys are enabled.
   1103 
   1104         @param a_pSection   Section to add or update
   1105         @param a_pKey       Key to add or update. 
   1106         @param a_bValue     Value to set. 
   1107         @param a_pComment   Comment to be associated with the key. See the 
   1108                             notes on SetValue() for comments.
   1109         @param a_bForceReplace  Should all existing values in a multi-key INI
   1110                             file be replaced with this entry. This option has
   1111                             no effect if not using multi-key files. The 
   1112                             difference between Delete/SetBoolValue and 
   1113                             SetBoolValue with a_bForceReplace = true, is that 
   1114                             the load order and comment will be preserved this 
   1115                             way.
   1116 
   1117         @return SI_Error    See error definitions
   1118         @return SI_UPDATED  Value was updated
   1119         @return SI_INSERTED Value was inserted
   1120      */
   1121     SI_Error SetBoolValue(
   1122         const SI_CHAR * a_pSection,
   1123         const SI_CHAR * a_pKey,
   1124         bool            a_bValue,
   1125         const SI_CHAR * a_pComment      = NULL,
   1126         bool            a_bForceReplace = false
   1127         );
   1128 
   1129     /** Delete an entire section, or a key from a section. Note that the
   1130         data returned by GetSection is invalid and must not be used after
   1131         anything has been deleted from that section using this method.
   1132         Note when multiple keys is enabled, this will delete all keys with
   1133         that name; to selectively delete individual key/values, use
   1134         DeleteValue.
   1135 
   1136         @param a_pSection       Section to delete key from, or if
   1137                                 a_pKey is NULL, the section to remove.
   1138         @param a_pKey           Key to remove from the section. Set to
   1139                                 NULL to remove the entire section.
   1140         @param a_bRemoveEmpty   If the section is empty after this key has
   1141                                 been deleted, should the empty section be
   1142                                 removed?
   1143 
   1144         @return true            Key or section was deleted.
   1145         @return false           Key or section was not found.
   1146      */
   1147     bool Delete(
   1148         const SI_CHAR * a_pSection,
   1149         const SI_CHAR * a_pKey,
   1150         bool            a_bRemoveEmpty = false
   1151         );
   1152 
   1153     /** Delete an entire section, or a key from a section. If value is
   1154         provided, only remove keys with the value. Note that the data
   1155         returned by GetSection is invalid and must not be used after
   1156         anything has been deleted from that section using this method.
   1157         Note when multiple keys is enabled, all keys with the value will
   1158         be deleted.
   1159 
   1160         @param a_pSection       Section to delete key from, or if
   1161                                 a_pKey is NULL, the section to remove.
   1162         @param a_pKey           Key to remove from the section. Set to
   1163                                 NULL to remove the entire section.
   1164         @param a_pValue         Value of key to remove from the section.
   1165                                 Set to NULL to remove all keys.
   1166         @param a_bRemoveEmpty   If the section is empty after this key has
   1167                                 been deleted, should the empty section be
   1168                                 removed?
   1169 
   1170         @return true            Key/value or section was deleted.
   1171         @return false           Key/value or section was not found.
   1172      */
   1173     bool DeleteValue(
   1174         const SI_CHAR * a_pSection,
   1175         const SI_CHAR * a_pKey,
   1176         const SI_CHAR * a_pValue,
   1177         bool            a_bRemoveEmpty = false
   1178         );
   1179 
   1180     /*-----------------------------------------------------------------------*/
   1181     /** @}
   1182         @{ @name Converter */
   1183 
   1184     /** Return a conversion object to convert text to the same encoding
   1185         as is used by the Save(), SaveFile() and SaveString() functions.
   1186         Use this to prepare the strings that you wish to append or prepend
   1187         to the output INI data.
   1188      */
   1189     Converter GetConverter() const {
   1190         return Converter(m_bStoreIsUtf8);
   1191     }
   1192 
   1193     /*-----------------------------------------------------------------------*/
   1194     /** @} */
   1195 
   1196 private:
   1197     // copying is not permitted
   1198     CSimpleIniTempl(const CSimpleIniTempl &); // disabled
   1199     CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled
   1200 
   1201     /** Parse the data looking for a file comment and store it if found.
   1202     */
   1203     SI_Error FindFileComment(
   1204         SI_CHAR *&      a_pData,
   1205         bool            a_bCopyStrings
   1206         );
   1207 
   1208     /** Parse the data looking for the next valid entry. The memory pointed to
   1209         by a_pData is modified by inserting NULL characters. The pointer is
   1210         updated to the current location in the block of text.
   1211     */
   1212     bool FindEntry(
   1213         SI_CHAR *&  a_pData,
   1214         const SI_CHAR *&  a_pSection,
   1215         const SI_CHAR *&  a_pKey,
   1216         const SI_CHAR *&  a_pVal,
   1217         const SI_CHAR *&  a_pComment
   1218         ) const;
   1219 
   1220     /** Add the section/key/value to our data.
   1221 
   1222         @param a_pSection   Section name. Sections will be created if they
   1223                             don't already exist.
   1224         @param a_pKey       Key name. May be NULL to create an empty section.
   1225                             Existing entries will be updated. New entries will
   1226                             be created.
   1227         @param a_pValue     Value for the key.
   1228         @param a_pComment   Comment to be associated with the section or the
   1229                             key. If a_pKey is NULL then it will be associated
   1230                             with the section, otherwise the key. This must be
   1231                             a string in full comment form already (have a
   1232                             comment character starting every line).
   1233         @param a_bForceReplace  Should all existing values in a multi-key INI
   1234                             file be replaced with this entry. This option has
   1235                             no effect if not using multi-key files. The 
   1236                             difference between Delete/AddEntry and AddEntry
   1237                             with a_bForceReplace = true, is that the load 
   1238                             order and comment will be preserved this way.
   1239         @param a_bCopyStrings   Should copies of the strings be made or not.
   1240                             If false then the pointers will be used as is.
   1241     */
   1242     SI_Error AddEntry(
   1243         const SI_CHAR * a_pSection,
   1244         const SI_CHAR * a_pKey,
   1245         const SI_CHAR * a_pValue,
   1246         const SI_CHAR * a_pComment,
   1247         bool            a_bForceReplace,
   1248         bool            a_bCopyStrings
   1249         );
   1250 
   1251     /** Is the supplied character a whitespace character? */
   1252     inline bool IsSpace(SI_CHAR ch) const {
   1253         return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
   1254     }
   1255 
   1256     /** Does the supplied character start a comment line? */
   1257     inline bool IsComment(SI_CHAR ch) const {
   1258         return (ch == ';' || ch == '#');
   1259     }
   1260 
   1261 
   1262     /** Skip over a newline character (or characters) for either DOS or UNIX */
   1263     inline void SkipNewLine(SI_CHAR *& a_pData) const {
   1264         a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1;
   1265     }
   1266 
   1267     /** Make a copy of the supplied string, replacing the original pointer */
   1268     SI_Error CopyString(const SI_CHAR *& a_pString);
   1269 
   1270     /** Delete a string from the copied strings buffer if necessary */
   1271     void DeleteString(const SI_CHAR * a_pString);
   1272 
   1273     /** Internal use of our string comparison function */
   1274     bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const {
   1275         const static SI_STRLESS isLess = SI_STRLESS();
   1276         return isLess(a_pLeft, a_pRight);
   1277     }
   1278 
   1279     bool IsMultiLineTag(const SI_CHAR * a_pData) const;
   1280     bool IsMultiLineData(const SI_CHAR * a_pData) const;
   1281     bool IsSingleLineQuotedValue(const SI_CHAR* a_pData) const;
   1282     bool LoadMultiLineText(
   1283         SI_CHAR *&          a_pData,
   1284         const SI_CHAR *&    a_pVal,
   1285         const SI_CHAR *     a_pTagName,
   1286         bool                a_bAllowBlankLinesInComment = false
   1287         ) const;
   1288     bool IsNewLineChar(SI_CHAR a_c) const;
   1289 
   1290     bool OutputMultiLineText(
   1291         OutputWriter &  a_oOutput,
   1292         Converter &     a_oConverter,
   1293         const SI_CHAR * a_pText
   1294         ) const;
   1295 
   1296 private:
   1297     /** Copy of the INI file data in our character format. This will be
   1298         modified when parsed to have NULL characters added after all
   1299         interesting string entries. All of the string pointers to sections,
   1300         keys and values point into this block of memory.
   1301      */
   1302     SI_CHAR * m_pData;
   1303 
   1304     /** Length of the data that we have stored. Used when deleting strings
   1305         to determine if the string is stored here or in the allocated string
   1306         buffer.
   1307      */
   1308     size_t m_uDataLen;
   1309 
   1310     /** File comment for this data, if one exists. */
   1311     const SI_CHAR * m_pFileComment;
   1312 
   1313     /** constant empty string */
   1314     const SI_CHAR m_cEmptyString;
   1315 
   1316     /** Parsed INI data. Section -> (Key -> Value). */
   1317     TSection m_data;
   1318 
   1319     /** This vector stores allocated memory for copies of strings that have
   1320         been supplied after the file load. It will be empty unless SetValue()
   1321         has been called.
   1322      */
   1323     TNamesDepend m_strings;
   1324 
   1325     /** Is the format of our datafile UTF-8 or MBCS? */
   1326     bool m_bStoreIsUtf8;
   1327 
   1328     /** Are multiple values permitted for the same key? */
   1329     bool m_bAllowMultiKey;
   1330 
   1331     /** Are data values permitted to span multiple lines? */
   1332     bool m_bAllowMultiLine;
   1333 
   1334     /** Should spaces be written out surrounding the equals sign? */
   1335     bool m_bSpaces;
   1336     
   1337     /** Should quoted data in values be recognized and parsed? */
   1338     bool m_bParseQuotes;
   1339 
   1340     /** Do keys always need to have an equals sign when reading/writing? */
   1341     bool m_bAllowKeyOnly;
   1342 
   1343     /** Next order value, used to ensure sections and keys are output in the
   1344         same order that they are loaded/added.
   1345      */
   1346     int m_nOrder;
   1347 };
   1348 
   1349 // ---------------------------------------------------------------------------
   1350 //                                  IMPLEMENTATION
   1351 // ---------------------------------------------------------------------------
   1352 
   1353 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1354 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl(
   1355     bool a_bIsUtf8,
   1356     bool a_bAllowMultiKey,
   1357     bool a_bAllowMultiLine
   1358     )
   1359   : m_pData(0)
   1360   , m_uDataLen(0)
   1361   , m_pFileComment(NULL)
   1362   , m_cEmptyString(0)
   1363   , m_bStoreIsUtf8(a_bIsUtf8)
   1364   , m_bAllowMultiKey(a_bAllowMultiKey)
   1365   , m_bAllowMultiLine(a_bAllowMultiLine)
   1366   , m_bSpaces(true)
   1367   , m_bParseQuotes(false)
   1368   , m_bAllowKeyOnly(false)
   1369   , m_nOrder(0)
   1370 { }
   1371 
   1372 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1373 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::~CSimpleIniTempl()
   1374 {
   1375     Reset();
   1376 }
   1377 
   1378 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1379 void
   1380 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Reset()
   1381 {
   1382     // remove all data
   1383     delete[] m_pData;
   1384     m_pData = NULL;
   1385     m_uDataLen = 0;
   1386     m_pFileComment = NULL;
   1387     if (!m_data.empty()) {
   1388         m_data.erase(m_data.begin(), m_data.end());
   1389     }
   1390 
   1391     // remove all strings
   1392     if (!m_strings.empty()) {
   1393         typename TNamesDepend::iterator i = m_strings.begin();
   1394         for (; i != m_strings.end(); ++i) {
   1395             delete[] const_cast<SI_CHAR*>(i->pItem);
   1396         }
   1397         m_strings.erase(m_strings.begin(), m_strings.end());
   1398     }
   1399 }
   1400 
   1401 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1402 SI_Error
   1403 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
   1404     const char * a_pszFile
   1405     )
   1406 {
   1407     FILE * fp = NULL;
   1408 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
   1409     fopen_s(&fp, a_pszFile, "rb");
   1410 #else // !__STDC_WANT_SECURE_LIB__
   1411     fp = fopen(a_pszFile, "rb");
   1412 #endif // __STDC_WANT_SECURE_LIB__
   1413     if (!fp) {
   1414         return SI_FILE;
   1415     }
   1416     SI_Error rc = LoadFile(fp);
   1417     fclose(fp);
   1418     return rc;
   1419 }
   1420 
   1421 #ifdef SI_HAS_WIDE_FILE
   1422 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1423 SI_Error
   1424 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
   1425     const SI_WCHAR_T * a_pwszFile
   1426     )
   1427 {
   1428 #ifdef _WIN32
   1429     FILE * fp = NULL;
   1430 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
   1431     _wfopen_s(&fp, a_pwszFile, L"rb");
   1432 #else // !__STDC_WANT_SECURE_LIB__
   1433     fp = _wfopen(a_pwszFile, L"rb");
   1434 #endif // __STDC_WANT_SECURE_LIB__
   1435     if (!fp) return SI_FILE;
   1436     SI_Error rc = LoadFile(fp);
   1437     fclose(fp);
   1438     return rc;
   1439 #else // !_WIN32 (therefore SI_CONVERT_ICU)
   1440     char szFile[256];
   1441     u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
   1442     return LoadFile(szFile);
   1443 #endif // _WIN32
   1444 }
   1445 #endif // SI_HAS_WIDE_FILE
   1446 
   1447 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1448 SI_Error
   1449 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
   1450     FILE * a_fpFile
   1451     )
   1452 {
   1453     // load the raw file data
   1454     int retval = fseek(a_fpFile, 0, SEEK_END);
   1455     if (retval != 0) {
   1456         return SI_FILE;
   1457     }
   1458     long lSize = ftell(a_fpFile);
   1459     if (lSize < 0) {
   1460         return SI_FILE;
   1461     }
   1462     if (lSize == 0) {
   1463         return SI_OK;
   1464     }
   1465     
   1466     // allocate and ensure NULL terminated
   1467     char * pData = new(std::nothrow) char[lSize+static_cast<size_t>(1)];
   1468     if (!pData) {
   1469         return SI_NOMEM;
   1470     }
   1471     pData[lSize] = 0;
   1472     
   1473     // load data into buffer
   1474     fseek(a_fpFile, 0, SEEK_SET);
   1475     size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
   1476     if (uRead != (size_t) lSize) {
   1477         delete[] pData;
   1478         return SI_FILE;
   1479     }
   1480 
   1481     // convert the raw data to unicode
   1482     SI_Error rc = LoadData(pData, uRead);
   1483     delete[] pData;
   1484     return rc;
   1485 }
   1486 
   1487 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1488 SI_Error
   1489 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(
   1490     const char *    a_pData,
   1491     size_t          a_uDataLen
   1492     )
   1493 {
   1494     if (!a_pData) {
   1495         return SI_OK;
   1496     }
   1497     
   1498     // if the UTF-8 BOM exists, consume it and set mode to unicode, if we have
   1499     // already loaded data and try to change mode half-way through then this will
   1500     // be ignored and we will assert in debug versions
   1501     if (a_uDataLen >= 3 && memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
   1502         a_pData    += 3;
   1503         a_uDataLen -= 3;
   1504         SI_ASSERT(m_bStoreIsUtf8 || !m_pData); // we don't expect mixed mode data
   1505         SetUnicode();
   1506     }
   1507 
   1508     if (a_uDataLen == 0) {
   1509         return SI_OK;
   1510     }
   1511 
   1512     // determine the length of the converted data
   1513     SI_CONVERTER converter(m_bStoreIsUtf8);
   1514     size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
   1515     if (uLen == (size_t)(-1)) {
   1516         return SI_FAIL;
   1517     }
   1518 
   1519     // allocate memory for the data, ensure that there is a NULL
   1520     // terminator wherever the converted data ends
   1521     SI_CHAR * pData = new(std::nothrow) SI_CHAR[uLen+1];
   1522     if (!pData) {
   1523         return SI_NOMEM;
   1524     }
   1525     memset(pData, 0, sizeof(SI_CHAR)*(uLen+1));
   1526 
   1527     // convert the data
   1528     if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
   1529         delete[] pData;
   1530         return SI_FAIL;
   1531     }
   1532 
   1533     // parse it
   1534     const static SI_CHAR empty = 0;
   1535     SI_CHAR * pWork = pData;
   1536     const SI_CHAR * pSection = &empty;
   1537     const SI_CHAR * pItem = NULL;
   1538     const SI_CHAR * pVal = NULL;
   1539     const SI_CHAR * pComment = NULL;
   1540 
   1541     // We copy the strings if we are loading data into this class when we
   1542     // already have stored some.
   1543     bool bCopyStrings = (m_pData != NULL);
   1544 
   1545     // find a file comment if it exists, this is a comment that starts at the
   1546     // beginning of the file and continues until the first blank line.
   1547     SI_Error rc = FindFileComment(pWork, bCopyStrings);
   1548     if (rc < 0) return rc;
   1549 
   1550     // add every entry in the file to the data table
   1551     while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
   1552         rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings);
   1553         if (rc < 0) return rc;
   1554     }
   1555 
   1556     // store these strings if we didn't copy them
   1557     if (bCopyStrings) {
   1558         delete[] pData;
   1559     }
   1560     else {
   1561         m_pData = pData;
   1562         m_uDataLen = uLen+1;
   1563     }
   1564 
   1565     return SI_OK;
   1566 }
   1567 
   1568 #ifdef SI_SUPPORT_IOSTREAMS
   1569 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1570 SI_Error
   1571 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(
   1572     std::istream & a_istream
   1573     )
   1574 {
   1575     std::string strData;
   1576     char szBuf[512];
   1577     do {
   1578         a_istream.get(szBuf, sizeof(szBuf), '\0');
   1579         strData.append(szBuf);
   1580     }
   1581     while (a_istream.good());
   1582     return LoadData(strData);
   1583 }
   1584 #endif // SI_SUPPORT_IOSTREAMS
   1585 
   1586 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1587 SI_Error
   1588 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindFileComment(
   1589     SI_CHAR *&      a_pData,
   1590     bool            a_bCopyStrings
   1591     )
   1592 {
   1593     // there can only be a single file comment
   1594     if (m_pFileComment) {
   1595         return SI_OK;
   1596     }
   1597 
   1598     // Load the file comment as multi-line text, this will modify all of
   1599     // the newline characters to be single \n chars
   1600     if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
   1601         return SI_OK;
   1602     }
   1603 
   1604     // copy the string if necessary
   1605     if (a_bCopyStrings) {
   1606         SI_Error rc = CopyString(m_pFileComment);
   1607         if (rc < 0) return rc;
   1608     }
   1609 
   1610     return SI_OK;
   1611 }
   1612 
   1613 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1614 bool
   1615 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
   1616     SI_CHAR *&        a_pData,
   1617     const SI_CHAR *&  a_pSection,
   1618     const SI_CHAR *&  a_pKey,
   1619     const SI_CHAR *&  a_pVal,
   1620     const SI_CHAR *&  a_pComment
   1621     ) const
   1622 {
   1623     a_pComment = NULL;
   1624 
   1625     bool bHaveValue = false;
   1626     SI_CHAR * pTrail = NULL;
   1627     while (*a_pData) {
   1628         // skip spaces and empty lines
   1629         while (*a_pData && IsSpace(*a_pData)) {
   1630             ++a_pData;
   1631         }
   1632         if (!*a_pData) {
   1633             break;
   1634         }
   1635 
   1636         // skip processing of comment lines but keep a pointer to
   1637         // the start of the comment.
   1638         if (IsComment(*a_pData)) {
   1639             LoadMultiLineText(a_pData, a_pComment, NULL, true);
   1640             continue;
   1641         }
   1642 
   1643         // process section names
   1644         if (*a_pData == '[') {
   1645             // skip leading spaces
   1646             ++a_pData;
   1647             while (*a_pData && IsSpace(*a_pData)) {
   1648                 ++a_pData;
   1649             }
   1650 
   1651             // find the end of the section name (it may contain spaces)
   1652             // and convert it to lowercase as necessary
   1653             a_pSection = a_pData;
   1654             while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
   1655                 ++a_pData;
   1656             }
   1657 
   1658             // if it's an invalid line, just skip it
   1659             if (*a_pData != ']') {
   1660                 continue;
   1661             }
   1662 
   1663             // remove trailing spaces from the section
   1664             pTrail = a_pData - 1;
   1665             while (pTrail >= a_pSection && IsSpace(*pTrail)) {
   1666                 --pTrail;
   1667             }
   1668             ++pTrail;
   1669             *pTrail = 0;
   1670 
   1671             // skip to the end of the line
   1672             ++a_pData;  // safe as checked that it == ']' above
   1673             while (*a_pData && !IsNewLineChar(*a_pData)) {
   1674                 ++a_pData;
   1675             }
   1676 
   1677             a_pKey = NULL;
   1678             a_pVal = NULL;
   1679             return true;
   1680         }
   1681 
   1682         // find the end of the key name (it may contain spaces)
   1683         a_pKey = a_pData;
   1684         while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
   1685             ++a_pData;
   1686         }
   1687         // *a_pData is null, equals, or newline
   1688 
   1689         // if no value and we don't allow no value, then invalid
   1690         bHaveValue = (*a_pData == '=');
   1691         if (!bHaveValue && !m_bAllowKeyOnly) {
   1692             continue;
   1693         }
   1694 
   1695         // empty keys are invalid
   1696         if (bHaveValue && a_pKey == a_pData) {
   1697             while (*a_pData && !IsNewLineChar(*a_pData)) {
   1698                 ++a_pData;
   1699             }
   1700             continue;
   1701         }
   1702 
   1703         // remove trailing spaces from the key
   1704         pTrail = a_pData - 1;
   1705         while (pTrail >= a_pKey && IsSpace(*pTrail)) {
   1706             --pTrail;
   1707         }
   1708         ++pTrail;
   1709 
   1710         if (bHaveValue) {
   1711             // process the value 
   1712             *pTrail = 0;
   1713 
   1714             // skip leading whitespace on the value
   1715             ++a_pData;  // safe as checked that it == '=' above
   1716             while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
   1717                 ++a_pData;
   1718             }
   1719 
   1720             // find the end of the value which is the end of this line
   1721             a_pVal = a_pData;
   1722             while (*a_pData && !IsNewLineChar(*a_pData)) {
   1723                 ++a_pData;
   1724             }
   1725 
   1726             // remove trailing spaces from the value
   1727             pTrail = a_pData - 1;
   1728             if (*a_pData) { // prepare for the next round
   1729                 SkipNewLine(a_pData);
   1730             }
   1731             while (pTrail >= a_pVal && IsSpace(*pTrail)) {
   1732                 --pTrail;
   1733             }
   1734             ++pTrail;
   1735             *pTrail = 0;
   1736 
   1737             // check for multi-line entries
   1738             if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
   1739                 // skip the "<<<" to get the tag that will end the multiline
   1740                 const SI_CHAR* pTagName = a_pVal + 3;
   1741                 return LoadMultiLineText(a_pData, a_pVal, pTagName);
   1742             }
   1743 
   1744             // check for quoted values, we are not supporting escapes in quoted values (yet)
   1745             if (m_bParseQuotes) {
   1746                 --pTrail;
   1747                 if (pTrail > a_pVal && *a_pVal == '"' && *pTrail == '"') {
   1748                     ++a_pVal;
   1749                     *pTrail = 0;
   1750                 }
   1751             }
   1752         }
   1753         else {
   1754             // no value to process, just prepare for the next
   1755             if (*a_pData) { 
   1756                 SkipNewLine(a_pData);
   1757             }
   1758             *pTrail = 0;
   1759         }
   1760 
   1761         // return the standard entry
   1762         return true;
   1763     }
   1764 
   1765     return false;
   1766 }
   1767 
   1768 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1769 bool
   1770 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineTag(
   1771     const SI_CHAR * a_pVal
   1772     ) const
   1773 {
   1774     // check for the "<<<" prefix for a multi-line entry
   1775     if (*a_pVal++ != '<') return false;
   1776     if (*a_pVal++ != '<') return false;
   1777     if (*a_pVal++ != '<') return false;
   1778     return true;
   1779 }
   1780 
   1781 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1782 bool
   1783 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData(
   1784     const SI_CHAR * a_pData
   1785     ) const
   1786 {
   1787     // data is multi-line if it has any of the following features:
   1788     //  * whitespace prefix
   1789     //  * embedded newlines
   1790     //  * whitespace suffix
   1791 
   1792     // empty string
   1793     if (!*a_pData) {
   1794         return false;
   1795     }
   1796 
   1797     // check for prefix
   1798     if (IsSpace(*a_pData)) {
   1799         return true;
   1800     }
   1801 
   1802     // embedded newlines
   1803     while (*a_pData) {
   1804         if (IsNewLineChar(*a_pData)) {
   1805             return true;
   1806         }
   1807         ++a_pData;
   1808     }
   1809 
   1810     // check for suffix
   1811     if (IsSpace(*--a_pData)) {
   1812         return true;
   1813     }
   1814 
   1815     return false;
   1816 }
   1817 
   1818 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1819 bool
   1820 CSimpleIniTempl<SI_CHAR, SI_STRLESS, SI_CONVERTER>::IsSingleLineQuotedValue(
   1821     const SI_CHAR* a_pData
   1822 ) const
   1823 {
   1824     // data needs quoting if it starts or ends with whitespace 
   1825     // and doesn't have embedded newlines
   1826 
   1827     // empty string
   1828     if (!*a_pData) {
   1829         return false;
   1830     }
   1831 
   1832     // check for prefix
   1833     if (IsSpace(*a_pData)) {
   1834         return true;
   1835     }
   1836 
   1837     // embedded newlines
   1838     while (*a_pData) {
   1839         if (IsNewLineChar(*a_pData)) {
   1840             return false;
   1841         }
   1842         ++a_pData;
   1843     }
   1844 
   1845     // check for suffix
   1846     if (IsSpace(*--a_pData)) {
   1847         return true;
   1848     }
   1849 
   1850     return false;
   1851 }
   1852 
   1853 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1854 bool
   1855 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar(
   1856     SI_CHAR a_c
   1857     ) const
   1858 {
   1859     return (a_c == '\n' || a_c == '\r');
   1860 }
   1861 
   1862 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1863 bool
   1864 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
   1865     SI_CHAR *&          a_pData,
   1866     const SI_CHAR *&    a_pVal,
   1867     const SI_CHAR *     a_pTagName,
   1868     bool                a_bAllowBlankLinesInComment
   1869     ) const
   1870 {
   1871     // we modify this data to strip all newlines down to a single '\n'
   1872     // character. This means that on Windows we need to strip out some
   1873     // characters which will make the data shorter.
   1874     // i.e.  LINE1-LINE1\r\nLINE2-LINE2\0 will become
   1875     //       LINE1-LINE1\nLINE2-LINE2\0
   1876     // The pDataLine entry is the pointer to the location in memory that
   1877     // the current line needs to start to run following the existing one.
   1878     // This may be the same as pCurrLine in which case no move is needed.
   1879     SI_CHAR * pDataLine = a_pData;
   1880     SI_CHAR * pCurrLine;
   1881 
   1882     // value starts at the current line
   1883     a_pVal = a_pData;
   1884 
   1885     // find the end tag. This tag must start in column 1 and be
   1886     // followed by a newline. We ignore any whitespace after the end
   1887     // tag but not whitespace before it.
   1888     SI_CHAR cEndOfLineChar = *a_pData;
   1889     for(;;) {
   1890         // if we are loading comments then we need a comment character as
   1891         // the first character on every line
   1892         if (!a_pTagName && !IsComment(*a_pData)) {
   1893             // if we aren't allowing blank lines then we're done
   1894             if (!a_bAllowBlankLinesInComment) {
   1895                 break;
   1896             }
   1897 
   1898             // if we are allowing blank lines then we only include them
   1899             // in this comment if another comment follows, so read ahead
   1900             // to find out.
   1901             SI_CHAR * pCurr = a_pData;
   1902             int nNewLines = 0;
   1903             while (IsSpace(*pCurr)) {
   1904                 if (IsNewLineChar(*pCurr)) {
   1905                     ++nNewLines;
   1906                     SkipNewLine(pCurr);
   1907                 }
   1908                 else {
   1909                     ++pCurr;
   1910                 }
   1911             }
   1912 
   1913             // we have a comment, add the blank lines to the output
   1914             // and continue processing from here
   1915             if (IsComment(*pCurr)) {
   1916                 for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n';
   1917                 a_pData = pCurr;
   1918                 continue;
   1919             }
   1920 
   1921             // the comment ends here
   1922             break;
   1923         }
   1924 
   1925         // find the end of this line
   1926         pCurrLine = a_pData;
   1927         while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
   1928 
   1929         // move this line down to the location that it should be if necessary
   1930         if (pDataLine < pCurrLine) {
   1931             size_t nLen = (size_t) (a_pData - pCurrLine);
   1932             memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
   1933             pDataLine[nLen] = '\0';
   1934         }
   1935 
   1936         // end the line with a NULL
   1937         cEndOfLineChar = *a_pData;
   1938         *a_pData = 0;
   1939 
   1940         // if are looking for a tag then do the check now. This is done before
   1941         // checking for end of the data, so that if we have the tag at the end
   1942         // of the data then the tag is removed correctly.
   1943         if (a_pTagName) {
   1944             // strip whitespace from the end of this tag
   1945             SI_CHAR* pc = a_pData - 1;
   1946             while (pc > pDataLine && IsSpace(*pc)) --pc;
   1947             SI_CHAR ch = *++pc;
   1948             *pc = 0;
   1949 
   1950             if (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)) {
   1951                 break;
   1952             }
   1953 
   1954             *pc = ch;
   1955         }
   1956 
   1957         // if we are at the end of the data then we just automatically end
   1958         // this entry and return the current data.
   1959         if (!cEndOfLineChar) {
   1960             return true;
   1961         }
   1962 
   1963         // otherwise we need to process this newline to ensure that it consists
   1964         // of just a single \n character.
   1965         pDataLine += (a_pData - pCurrLine);
   1966         *a_pData = cEndOfLineChar;
   1967         SkipNewLine(a_pData);
   1968         *pDataLine++ = '\n';
   1969     }
   1970 
   1971     // if we didn't find a comment at all then return false
   1972     if (a_pVal == a_pData) {
   1973         a_pVal = NULL;
   1974         return false;
   1975     }
   1976 
   1977     // the data (which ends at the end of the last line) needs to be
   1978     // null-terminated BEFORE before the newline character(s). If the
   1979     // user wants a new line in the multi-line data then they need to
   1980     // add an empty line before the tag.
   1981     *--pDataLine = '\0';
   1982 
   1983     // if looking for a tag and if we aren't at the end of the data,
   1984     // then move a_pData to the start of the next line.
   1985     if (a_pTagName && cEndOfLineChar) {
   1986         SI_ASSERT(IsNewLineChar(cEndOfLineChar));
   1987         *a_pData = cEndOfLineChar;
   1988         SkipNewLine(a_pData);
   1989     }
   1990 
   1991     return true;
   1992 }
   1993 
   1994 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   1995 SI_Error
   1996 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString(
   1997     const SI_CHAR *& a_pString
   1998     )
   1999 {
   2000     size_t uLen = 0;
   2001     if (sizeof(SI_CHAR) == sizeof(char)) {
   2002         uLen = strlen((const char *)a_pString);
   2003     }
   2004     else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
   2005         uLen = wcslen((const wchar_t *)a_pString);
   2006     }
   2007     else {
   2008         for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
   2009     }
   2010     ++uLen; // NULL character
   2011     SI_CHAR * pCopy = new(std::nothrow) SI_CHAR[uLen];
   2012     if (!pCopy) {
   2013         return SI_NOMEM;
   2014     }
   2015     memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
   2016     m_strings.push_back(pCopy);
   2017     a_pString = pCopy;
   2018     return SI_OK;
   2019 }
   2020 
   2021 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2022 SI_Error
   2023 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
   2024     const SI_CHAR * a_pSection,
   2025     const SI_CHAR * a_pKey,
   2026     const SI_CHAR * a_pValue,
   2027     const SI_CHAR * a_pComment,
   2028     bool            a_bForceReplace,
   2029     bool            a_bCopyStrings
   2030     )
   2031 {
   2032     SI_Error rc;
   2033     bool bInserted = false;
   2034 
   2035     SI_ASSERT(!a_pComment || IsComment(*a_pComment));
   2036 
   2037     // if we are copying strings then make a copy of the comment now
   2038     // because we will need it when we add the entry.
   2039     if (a_bCopyStrings && a_pComment) {
   2040         rc = CopyString(a_pComment);
   2041         if (rc < 0) return rc;
   2042     }
   2043 
   2044     // create the section entry if necessary
   2045     typename TSection::iterator iSection = m_data.find(a_pSection);
   2046     if (iSection == m_data.end()) {
   2047         // if the section doesn't exist then we need a copy as the
   2048         // string needs to last beyond the end of this function
   2049         if (a_bCopyStrings) {
   2050             rc = CopyString(a_pSection);
   2051             if (rc < 0) return rc;
   2052         }
   2053 
   2054         // only set the comment if this is a section only entry
   2055         Entry oSection(a_pSection, ++m_nOrder);
   2056         if (a_pComment && !a_pKey) {
   2057             oSection.pComment = a_pComment;
   2058         }
   2059 
   2060         typename TSection::value_type oEntry(oSection, TKeyVal());
   2061         typedef typename TSection::iterator SectionIterator;
   2062         std::pair<SectionIterator,bool> i = m_data.insert(oEntry);
   2063         iSection = i.first;
   2064         bInserted = true;
   2065     }
   2066     if (!a_pKey) {
   2067         // section only entries are specified with pItem as NULL
   2068         return bInserted ? SI_INSERTED : SI_UPDATED;
   2069     }
   2070 
   2071     // check for existence of the key
   2072     TKeyVal & keyval = iSection->second;
   2073     typename TKeyVal::iterator iKey = keyval.find(a_pKey);
   2074     bInserted = iKey == keyval.end();
   2075 
   2076     // remove all existing entries but save the load order and
   2077     // comment of the first entry
   2078     int nLoadOrder = ++m_nOrder;
   2079     if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) {
   2080         const SI_CHAR * pComment = NULL;
   2081         while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) {
   2082             if (iKey->first.nOrder < nLoadOrder) {
   2083                 nLoadOrder = iKey->first.nOrder;
   2084                 pComment   = iKey->first.pComment;
   2085             }
   2086             ++iKey;
   2087         }
   2088         if (pComment) {
   2089             DeleteString(a_pComment);
   2090             a_pComment = pComment;
   2091             CopyString(a_pComment);
   2092         }
   2093         Delete(a_pSection, a_pKey);
   2094         iKey = keyval.end();
   2095     }
   2096 
   2097     // values need to be a valid string, even if they are an empty string
   2098     if (!a_pValue) {
   2099         a_pValue = &m_cEmptyString;
   2100     }
   2101 
   2102     // make string copies if necessary
   2103     bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
   2104     if (a_bCopyStrings) {
   2105         if (bForceCreateNewKey || iKey == keyval.end()) {
   2106             // if the key doesn't exist then we need a copy as the
   2107             // string needs to last beyond the end of this function
   2108             // because we will be inserting the key next
   2109             rc = CopyString(a_pKey);
   2110             if (rc < 0) return rc;
   2111         }
   2112 
   2113         // we always need a copy of the value
   2114         rc = CopyString(a_pValue);
   2115         if (rc < 0) return rc;
   2116     }
   2117 
   2118     // create the key entry
   2119     if (iKey == keyval.end() || bForceCreateNewKey) {
   2120         Entry oKey(a_pKey, nLoadOrder);
   2121         if (a_pComment) {
   2122             oKey.pComment = a_pComment;
   2123         }
   2124         typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL));
   2125         iKey = keyval.insert(oEntry);
   2126     }
   2127 
   2128     iKey->second = a_pValue;
   2129     return bInserted ? SI_INSERTED : SI_UPDATED;
   2130 }
   2131 
   2132 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2133 const SI_CHAR *
   2134 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetValue(
   2135     const SI_CHAR * a_pSection,
   2136     const SI_CHAR * a_pKey,
   2137     const SI_CHAR * a_pDefault,
   2138     bool *          a_pHasMultiple
   2139     ) const
   2140 {
   2141     if (a_pHasMultiple) {
   2142         *a_pHasMultiple = false;
   2143     }
   2144     if (!a_pSection || !a_pKey) {
   2145         return a_pDefault;
   2146     }
   2147     typename TSection::const_iterator iSection = m_data.find(a_pSection);
   2148     if (iSection == m_data.end()) {
   2149         return a_pDefault;
   2150     }
   2151     typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
   2152     if (iKeyVal == iSection->second.end()) {
   2153         return a_pDefault;
   2154     }
   2155 
   2156     // check for multiple entries with the same key
   2157     if (m_bAllowMultiKey && a_pHasMultiple) {
   2158         typename TKeyVal::const_iterator iTemp = iKeyVal;
   2159         if (++iTemp != iSection->second.end()) {
   2160             if (!IsLess(a_pKey, iTemp->first.pItem)) {
   2161                 *a_pHasMultiple = true;
   2162             }
   2163         }
   2164     }
   2165 
   2166     return iKeyVal->second;
   2167 }
   2168 
   2169 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2170 long
   2171 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetLongValue(
   2172     const SI_CHAR * a_pSection,
   2173     const SI_CHAR * a_pKey,
   2174     long            a_nDefault,
   2175     bool *          a_pHasMultiple
   2176     ) const
   2177 {
   2178     // return the default if we don't have a value
   2179     const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
   2180     if (!pszValue || !*pszValue) return a_nDefault;
   2181 
   2182     // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
   2183     char szValue[64] = { 0 };
   2184     SI_CONVERTER c(m_bStoreIsUtf8);
   2185     if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
   2186         return a_nDefault;
   2187     }
   2188 
   2189     // handle the value as hex if prefaced with "0x"
   2190     long nValue = a_nDefault;
   2191     char * pszSuffix = szValue;
   2192     if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
   2193         if (!szValue[2]) return a_nDefault;
   2194         nValue = strtol(&szValue[2], &pszSuffix, 16);
   2195     }
   2196     else {
   2197         nValue = strtol(szValue, &pszSuffix, 10);
   2198     }
   2199 
   2200     // any invalid strings will return the default value
   2201     if (*pszSuffix) { 
   2202         return a_nDefault; 
   2203     }
   2204 
   2205     return nValue;
   2206 }
   2207 
   2208 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2209 SI_Error 
   2210 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue(
   2211     const SI_CHAR * a_pSection,
   2212     const SI_CHAR * a_pKey,
   2213     long            a_nValue,
   2214     const SI_CHAR * a_pComment,
   2215     bool            a_bUseHex,
   2216     bool            a_bForceReplace
   2217     )
   2218 {
   2219     // use SetValue to create sections
   2220     if (!a_pSection || !a_pKey) return SI_FAIL;
   2221 
   2222     // convert to an ASCII string
   2223     char szInput[64];
   2224 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
   2225     sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
   2226 #else // !__STDC_WANT_SECURE_LIB__
   2227     snprintf(szInput, sizeof(szInput), a_bUseHex ? "0x%lx" : "%ld", a_nValue);
   2228 #endif // __STDC_WANT_SECURE_LIB__
   2229 
   2230     // convert to output text
   2231     SI_CHAR szOutput[64];
   2232     SI_CONVERTER c(m_bStoreIsUtf8);
   2233     c.ConvertFromStore(szInput, strlen(szInput) + 1, 
   2234         szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
   2235 
   2236     // actually add it
   2237     return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
   2238 }
   2239 
   2240 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2241 double
   2242 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetDoubleValue(
   2243     const SI_CHAR * a_pSection,
   2244     const SI_CHAR * a_pKey,
   2245     double          a_nDefault,
   2246     bool *          a_pHasMultiple
   2247     ) const
   2248 {
   2249     // return the default if we don't have a value
   2250     const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
   2251     if (!pszValue || !*pszValue) return a_nDefault;
   2252 
   2253     // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
   2254     char szValue[64] = { 0 };
   2255     SI_CONVERTER c(m_bStoreIsUtf8);
   2256     if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
   2257         return a_nDefault;
   2258     }
   2259 
   2260     char * pszSuffix = NULL;
   2261     double nValue = strtod(szValue, &pszSuffix);
   2262 
   2263     // any invalid strings will return the default value
   2264     if (!pszSuffix || *pszSuffix) { 
   2265         return a_nDefault; 
   2266     }
   2267 
   2268     return nValue;
   2269 }
   2270 
   2271 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2272 SI_Error 
   2273 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetDoubleValue(
   2274     const SI_CHAR * a_pSection,
   2275     const SI_CHAR * a_pKey,
   2276     double          a_nValue,
   2277     const SI_CHAR * a_pComment,
   2278     bool            a_bForceReplace
   2279     )
   2280 {
   2281     // use SetValue to create sections
   2282     if (!a_pSection || !a_pKey) return SI_FAIL;
   2283 
   2284     // convert to an ASCII string
   2285     char szInput[64];
   2286 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
   2287     sprintf_s(szInput, "%f", a_nValue);
   2288 #else // !__STDC_WANT_SECURE_LIB__
   2289     snprintf(szInput, sizeof(szInput), "%f", a_nValue);
   2290 #endif // __STDC_WANT_SECURE_LIB__
   2291 
   2292     // convert to output text
   2293     SI_CHAR szOutput[64];
   2294     SI_CONVERTER c(m_bStoreIsUtf8);
   2295     c.ConvertFromStore(szInput, strlen(szInput) + 1, 
   2296         szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
   2297 
   2298     // actually add it
   2299     return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
   2300 }
   2301 
   2302 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2303 bool
   2304 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetBoolValue(
   2305     const SI_CHAR * a_pSection,
   2306     const SI_CHAR * a_pKey,
   2307     bool            a_bDefault,
   2308     bool *          a_pHasMultiple
   2309     ) const
   2310 {
   2311     // return the default if we don't have a value
   2312     const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
   2313     if (!pszValue || !*pszValue) return a_bDefault;
   2314 
   2315     // we only look at the minimum number of characters
   2316     switch (pszValue[0]) {
   2317     case 't': case 'T': // true
   2318     case 'y': case 'Y': // yes
   2319     case '1':           // 1 (one)
   2320         return true;
   2321 
   2322     case 'f': case 'F': // false
   2323     case 'n': case 'N': // no
   2324     case '0':           // 0 (zero)
   2325         return false;
   2326 
   2327     case 'o': case 'O':
   2328         if (pszValue[1] == 'n' || pszValue[1] == 'N') return true;  // on
   2329         if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off
   2330         break;
   2331     }
   2332 
   2333     // no recognized value, return the default
   2334     return a_bDefault;
   2335 }
   2336 
   2337 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2338 SI_Error 
   2339 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetBoolValue(
   2340     const SI_CHAR * a_pSection,
   2341     const SI_CHAR * a_pKey,
   2342     bool            a_bValue,
   2343     const SI_CHAR * a_pComment,
   2344     bool            a_bForceReplace
   2345     )
   2346 {
   2347     // use SetValue to create sections
   2348     if (!a_pSection || !a_pKey) return SI_FAIL;
   2349 
   2350     // convert to an ASCII string
   2351     const char * pszInput = a_bValue ? "true" : "false";
   2352 
   2353     // convert to output text
   2354     SI_CHAR szOutput[64];
   2355     SI_CONVERTER c(m_bStoreIsUtf8);
   2356     c.ConvertFromStore(pszInput, strlen(pszInput) + 1, 
   2357         szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
   2358 
   2359     // actually add it
   2360     return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
   2361 }
   2362     
   2363 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2364 bool
   2365 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues(
   2366     const SI_CHAR * a_pSection,
   2367     const SI_CHAR * a_pKey,
   2368     TNamesDepend &  a_values
   2369     ) const
   2370 {
   2371     a_values.clear();
   2372 
   2373     if (!a_pSection || !a_pKey) {
   2374         return false;
   2375     }
   2376     typename TSection::const_iterator iSection = m_data.find(a_pSection);
   2377     if (iSection == m_data.end()) {
   2378         return false;
   2379     }
   2380     typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
   2381     if (iKeyVal == iSection->second.end()) {
   2382         return false;
   2383     }
   2384 
   2385     // insert all values for this key
   2386     a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
   2387     if (m_bAllowMultiKey) {
   2388         ++iKeyVal;
   2389         while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {
   2390             a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
   2391             ++iKeyVal;
   2392         }
   2393     }
   2394 
   2395     return true;
   2396 }
   2397 
   2398 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2399 int
   2400 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSectionSize(
   2401     const SI_CHAR * a_pSection
   2402     ) const
   2403 {
   2404     if (!a_pSection) {
   2405         return -1;
   2406     }
   2407 
   2408     typename TSection::const_iterator iSection = m_data.find(a_pSection);
   2409     if (iSection == m_data.end()) {
   2410         return -1;
   2411     }
   2412     const TKeyVal & section = iSection->second;
   2413 
   2414     // if multi-key isn't permitted then the section size is
   2415     // the number of keys that we have.
   2416     if (!m_bAllowMultiKey || section.empty()) {
   2417         return (int) section.size();
   2418     }
   2419 
   2420     // otherwise we need to count them
   2421     int nCount = 0;
   2422     const SI_CHAR * pLastKey = NULL;
   2423     typename TKeyVal::const_iterator iKeyVal = section.begin();
   2424     for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
   2425         if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
   2426             ++nCount;
   2427             pLastKey = iKeyVal->first.pItem;
   2428         }
   2429     }
   2430     return nCount;
   2431 }
   2432 
   2433 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2434 const typename CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::TKeyVal *
   2435 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSection(
   2436     const SI_CHAR * a_pSection
   2437     ) const
   2438 {
   2439     if (a_pSection) {
   2440         typename TSection::const_iterator i = m_data.find(a_pSection);
   2441         if (i != m_data.end()) {
   2442             return &(i->second);
   2443         }
   2444     }
   2445     return 0;
   2446 }
   2447 
   2448 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2449 size_t
   2450 CSimpleIniTempl<SI_CHAR, SI_STRLESS, SI_CONVERTER>::GetKeyCount() const
   2451 {
   2452   size_t count = 0;
   2453   typename TSection::const_iterator i = m_data.begin();
   2454   for (; i != m_data.end(); ++i)
   2455     count += i->second.size();
   2456   return count;
   2457 }
   2458 
   2459 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2460 void
   2461 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections(
   2462     TNamesDepend & a_names
   2463     ) const
   2464 {
   2465     a_names.clear();
   2466     typename TSection::const_iterator i = m_data.begin();
   2467     for (int n = 0; i != m_data.end(); ++i, ++n ) {
   2468         a_names.push_back(i->first);
   2469     }
   2470 }
   2471 
   2472 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2473 bool
   2474 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllKeys(
   2475     const SI_CHAR * a_pSection,
   2476     TNamesDepend &  a_names
   2477     ) const
   2478 {
   2479     a_names.clear();
   2480 
   2481     if (!a_pSection) {
   2482         return false;
   2483     }
   2484 
   2485     typename TSection::const_iterator iSection = m_data.find(a_pSection);
   2486     if (iSection == m_data.end()) {
   2487         return false;
   2488     }
   2489 
   2490     const TKeyVal & section = iSection->second;
   2491     const SI_CHAR * pLastKey = NULL;
   2492     typename TKeyVal::const_iterator iKeyVal = section.begin();
   2493     for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) {
   2494         if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
   2495             a_names.push_back(iKeyVal->first);
   2496             pLastKey = iKeyVal->first.pItem;
   2497         }
   2498     }
   2499 
   2500     return true;
   2501 }
   2502 
   2503 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2504 SI_Error
   2505 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
   2506     const char *    a_pszFile,
   2507     bool            a_bAddSignature
   2508     ) const
   2509 {
   2510     FILE * fp = NULL;
   2511 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
   2512     fopen_s(&fp, a_pszFile, "wb");
   2513 #else // !__STDC_WANT_SECURE_LIB__
   2514     fp = fopen(a_pszFile, "wb");
   2515 #endif // __STDC_WANT_SECURE_LIB__
   2516     if (!fp) return SI_FILE;
   2517     SI_Error rc = SaveFile(fp, a_bAddSignature);
   2518     fclose(fp);
   2519     return rc;
   2520 }
   2521 
   2522 #ifdef SI_HAS_WIDE_FILE
   2523 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2524 SI_Error
   2525 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
   2526     const SI_WCHAR_T *  a_pwszFile,
   2527     bool                a_bAddSignature
   2528     ) const
   2529 {
   2530 #ifdef _WIN32
   2531     FILE * fp = NULL;
   2532 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
   2533     _wfopen_s(&fp, a_pwszFile, L"wb");
   2534 #else // !__STDC_WANT_SECURE_LIB__
   2535     fp = _wfopen(a_pwszFile, L"wb");
   2536 #endif // __STDC_WANT_SECURE_LIB__
   2537     if (!fp) return SI_FILE;
   2538     SI_Error rc = SaveFile(fp, a_bAddSignature);
   2539     fclose(fp);
   2540     return rc;
   2541 #else // !_WIN32 (therefore SI_CONVERT_ICU)
   2542     char szFile[256];
   2543     u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
   2544     return SaveFile(szFile, a_bAddSignature);
   2545 #endif // _WIN32
   2546 }
   2547 #endif // SI_HAS_WIDE_FILE
   2548 
   2549 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2550 SI_Error
   2551 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
   2552     FILE *  a_pFile,
   2553     bool    a_bAddSignature
   2554     ) const
   2555 {
   2556     FileWriter writer(a_pFile);
   2557     return Save(writer, a_bAddSignature);
   2558 }
   2559 
   2560 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2561 SI_Error
   2562 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
   2563     OutputWriter &  a_oOutput,
   2564     bool            a_bAddSignature
   2565     ) const
   2566 {
   2567     Converter convert(m_bStoreIsUtf8);
   2568 
   2569     // add the UTF-8 signature if it is desired
   2570     if (m_bStoreIsUtf8 && a_bAddSignature) {
   2571         a_oOutput.Write(SI_UTF8_SIGNATURE);
   2572     }
   2573 
   2574     // get all of the sections sorted in load order
   2575     TNamesDepend oSections;
   2576     GetAllSections(oSections);
   2577 #if defined(_MSC_VER) && _MSC_VER <= 1200
   2578     oSections.sort();
   2579 #elif defined(__BORLANDC__)
   2580     oSections.sort(Entry::LoadOrder());
   2581 #else
   2582     oSections.sort(typename Entry::LoadOrder());
   2583 #endif
   2584 
   2585     // if there is an empty section name, then it must be written out first
   2586     // regardless of the load order
   2587     typename TNamesDepend::iterator is = oSections.begin();
   2588     for (; is != oSections.end(); ++is) {
   2589         if (!*is->pItem) {
   2590             // move the empty section name to the front of the section list
   2591             if (is != oSections.begin()) {
   2592                 oSections.splice(oSections.begin(), oSections, is, std::next(is));
   2593             }
   2594             break;
   2595         }
   2596     }
   2597 
   2598     // write the file comment if we have one
   2599     bool bNeedNewLine = false;
   2600     if (m_pFileComment) {
   2601         if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
   2602             return SI_FAIL;
   2603         }
   2604         bNeedNewLine = true;
   2605     }
   2606 
   2607     // iterate through our sections and output the data
   2608     typename TNamesDepend::const_iterator iSection = oSections.begin();
   2609     for ( ; iSection != oSections.end(); ++iSection ) {
   2610         // write out the comment if there is one
   2611         if (iSection->pComment) {
   2612             if (bNeedNewLine) {
   2613                 a_oOutput.Write(SI_NEWLINE_A);
   2614                 a_oOutput.Write(SI_NEWLINE_A);
   2615             }
   2616             if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) {
   2617                 return SI_FAIL;
   2618             }
   2619             bNeedNewLine = false;
   2620         }
   2621 
   2622         if (bNeedNewLine) {
   2623             a_oOutput.Write(SI_NEWLINE_A);
   2624             a_oOutput.Write(SI_NEWLINE_A);
   2625             bNeedNewLine = false;
   2626         }
   2627 
   2628         // write the section (unless there is no section name)
   2629         if (*iSection->pItem) {
   2630             if (!convert.ConvertToStore(iSection->pItem)) {
   2631                 return SI_FAIL;
   2632             }
   2633             a_oOutput.Write("[");
   2634             a_oOutput.Write(convert.Data());
   2635             a_oOutput.Write("]");
   2636             a_oOutput.Write(SI_NEWLINE_A);
   2637         }
   2638 
   2639         // get all of the keys sorted in load order
   2640         TNamesDepend oKeys;
   2641         GetAllKeys(iSection->pItem, oKeys);
   2642 #if defined(_MSC_VER) && _MSC_VER <= 1200
   2643         oKeys.sort();
   2644 #elif defined(__BORLANDC__)
   2645         oKeys.sort(Entry::LoadOrder());
   2646 #else
   2647         oKeys.sort(typename Entry::LoadOrder());
   2648 #endif
   2649 
   2650         // write all keys and values
   2651         typename TNamesDepend::const_iterator iKey = oKeys.begin();
   2652         for ( ; iKey != oKeys.end(); ++iKey) {
   2653             // get all values for this key
   2654             TNamesDepend oValues;
   2655             GetAllValues(iSection->pItem, iKey->pItem, oValues);
   2656 
   2657             typename TNamesDepend::const_iterator iValue = oValues.begin();
   2658             for ( ; iValue != oValues.end(); ++iValue) {
   2659                 // write out the comment if there is one
   2660                 if (iValue->pComment) {
   2661                     a_oOutput.Write(SI_NEWLINE_A);
   2662                     if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) {
   2663                         return SI_FAIL;
   2664                     }
   2665                 }
   2666 
   2667                 // write the key
   2668                 if (!convert.ConvertToStore(iKey->pItem)) {
   2669                     return SI_FAIL;
   2670                 }
   2671                 a_oOutput.Write(convert.Data());
   2672 
   2673                 // write the value as long 
   2674                 if (*iValue->pItem || !m_bAllowKeyOnly) {
   2675                     if (!convert.ConvertToStore(iValue->pItem)) {
   2676                         return SI_FAIL;
   2677                     }
   2678                     a_oOutput.Write(m_bSpaces ? " = " : "=");
   2679                     if (m_bParseQuotes && IsSingleLineQuotedValue(iValue->pItem)) {
   2680                         // the only way to preserve external whitespace on a value (i.e. before or after)
   2681                         // is to quote it. This is simple quoting, we don't escape quotes within the data. 
   2682                         a_oOutput.Write("\"");
   2683                         a_oOutput.Write(convert.Data());
   2684                         a_oOutput.Write("\"");
   2685                     }
   2686                     else if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
   2687                         // multi-line data needs to be processed specially to ensure
   2688                         // that we use the correct newline format for the current system
   2689                         a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
   2690                         if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
   2691                             return SI_FAIL;
   2692                         }
   2693                         a_oOutput.Write("END_OF_TEXT");
   2694                     }
   2695                     else {
   2696                         a_oOutput.Write(convert.Data());
   2697                     }
   2698                 }
   2699                 a_oOutput.Write(SI_NEWLINE_A);
   2700             }
   2701         }
   2702 
   2703         bNeedNewLine = true;
   2704     }
   2705 
   2706     return SI_OK;
   2707 }
   2708 
   2709 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2710 bool
   2711 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::OutputMultiLineText(
   2712     OutputWriter &  a_oOutput,
   2713     Converter &     a_oConverter,
   2714     const SI_CHAR * a_pText
   2715     ) const
   2716 {
   2717     const SI_CHAR * pEndOfLine;
   2718     SI_CHAR cEndOfLineChar = *a_pText;
   2719     while (cEndOfLineChar) {
   2720         // find the end of this line
   2721         pEndOfLine = a_pText;
   2722         for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;
   2723         cEndOfLineChar = *pEndOfLine;
   2724 
   2725         // temporarily null terminate, convert and output the line
   2726         *const_cast<SI_CHAR*>(pEndOfLine) = 0;
   2727         if (!a_oConverter.ConvertToStore(a_pText)) {
   2728             return false;
   2729         }
   2730         *const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar;
   2731         a_pText += (pEndOfLine - a_pText) + 1;
   2732         a_oOutput.Write(a_oConverter.Data());
   2733         a_oOutput.Write(SI_NEWLINE_A);
   2734     }
   2735     return true;
   2736 }
   2737 
   2738 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2739 bool
   2740 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(
   2741     const SI_CHAR * a_pSection,
   2742     const SI_CHAR * a_pKey,
   2743     bool            a_bRemoveEmpty
   2744     )
   2745 {
   2746     return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty);
   2747 }
   2748 
   2749 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2750 bool
   2751 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteValue(
   2752     const SI_CHAR * a_pSection,
   2753     const SI_CHAR * a_pKey,
   2754     const SI_CHAR * a_pValue,
   2755     bool            a_bRemoveEmpty
   2756     )
   2757 {
   2758     if (!a_pSection) {
   2759         return false;
   2760     }
   2761 
   2762     typename TSection::iterator iSection = m_data.find(a_pSection);
   2763     if (iSection == m_data.end()) {
   2764         return false;
   2765     }
   2766 
   2767     // remove a single key if we have a keyname
   2768     if (a_pKey) {
   2769         typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
   2770         if (iKeyVal == iSection->second.end()) {
   2771             return false;
   2772         }
   2773 
   2774         const static SI_STRLESS isLess = SI_STRLESS();
   2775 
   2776         // remove any copied strings and then the key
   2777         typename TKeyVal::iterator iDelete;
   2778         bool bDeleted = false;
   2779         do {
   2780             iDelete = iKeyVal++;
   2781 
   2782             if(a_pValue == NULL ||
   2783             (isLess(a_pValue, iDelete->second) == false &&
   2784             isLess(iDelete->second, a_pValue) == false)) {
   2785                 DeleteString(iDelete->first.pItem);
   2786                 DeleteString(iDelete->second);
   2787                 iSection->second.erase(iDelete);
   2788                 bDeleted = true;
   2789             }
   2790         }
   2791         while (iKeyVal != iSection->second.end()
   2792             && !IsLess(a_pKey, iKeyVal->first.pItem));
   2793 
   2794         if(!bDeleted) {
   2795             return false;
   2796         }
   2797 
   2798         // done now if the section is not empty or we are not pruning away
   2799         // the empty sections. Otherwise let it fall through into the section
   2800         // deletion code
   2801         if (!a_bRemoveEmpty || !iSection->second.empty()) {
   2802             return true;
   2803         }
   2804     }
   2805     else {
   2806         // delete all copied strings from this section. The actual
   2807         // entries will be removed when the section is removed.
   2808         typename TKeyVal::iterator iKeyVal = iSection->second.begin();
   2809         for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
   2810             DeleteString(iKeyVal->first.pItem);
   2811             DeleteString(iKeyVal->second);
   2812         }
   2813     }
   2814 
   2815     // delete the section itself
   2816     DeleteString(iSection->first.pItem);
   2817     m_data.erase(iSection);
   2818 
   2819     return true;
   2820 }
   2821 
   2822 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
   2823 void
   2824 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString(
   2825     const SI_CHAR * a_pString
   2826     )
   2827 {
   2828     // strings may exist either inside the data block, or they will be
   2829     // individually allocated and stored in m_strings. We only physically
   2830     // delete those stored in m_strings.
   2831     if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
   2832         typename TNamesDepend::iterator i = m_strings.begin();
   2833         for (;i != m_strings.end(); ++i) {
   2834             if (a_pString == i->pItem) {
   2835                 delete[] const_cast<SI_CHAR*>(i->pItem);
   2836                 m_strings.erase(i);
   2837                 break;
   2838             }
   2839         }
   2840     }
   2841 }
   2842 
   2843 // ---------------------------------------------------------------------------
   2844 //                              CONVERSION FUNCTIONS
   2845 // ---------------------------------------------------------------------------
   2846 
   2847 // Defines the conversion classes for different libraries. Before including
   2848 // SimpleIni.h, set the converter that you wish you use by defining one of the
   2849 // following symbols.
   2850 //
   2851 //  SI_NO_CONVERSION        Do not make the "W" wide character version of the 
   2852 //                          library available. Only CSimpleIniA etc is defined.
   2853 //  SI_CONVERT_GENERIC      Use the Unicode reference conversion library in
   2854 //                          the accompanying files ConvertUTF.h/c
   2855 //  SI_CONVERT_ICU          Use the IBM ICU conversion library. Requires
   2856 //                          ICU headers on include path and icuuc.lib
   2857 //  SI_CONVERT_WIN32        Use the Win32 API functions for conversion.
   2858 
   2859 #if !defined(SI_NO_CONVERSION) && !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
   2860 # ifdef _WIN32
   2861 #  define SI_CONVERT_WIN32
   2862 # else
   2863 #  define SI_CONVERT_GENERIC
   2864 # endif
   2865 #endif
   2866 
   2867 /**
   2868  * Generic case-sensitive less than comparison. This class returns numerically
   2869  * ordered ASCII case-sensitive text for all possible sizes and types of
   2870  * SI_CHAR.
   2871  */
   2872 template<class SI_CHAR>
   2873 struct SI_GenericCase {
   2874     bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
   2875         long cmp;
   2876         for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
   2877             cmp = (long) *pLeft - (long) *pRight;
   2878             if (cmp != 0) {
   2879                 return cmp < 0;
   2880             }
   2881         }
   2882         return *pRight != 0;
   2883     }
   2884 };
   2885 
   2886 /**
   2887  * Generic ASCII case-insensitive less than comparison. This class returns
   2888  * numerically ordered ASCII case-insensitive text for all possible sizes
   2889  * and types of SI_CHAR. It is not safe for MBCS text comparison where
   2890  * ASCII A-Z characters are used in the encoding of multi-byte characters.
   2891  */
   2892 template<class SI_CHAR>
   2893 struct SI_GenericNoCase {
   2894     inline SI_CHAR locase(SI_CHAR ch) const {
   2895         return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
   2896     }
   2897     bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
   2898         long cmp;
   2899         for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
   2900             cmp = (long) locase(*pLeft) - (long) locase(*pRight);
   2901             if (cmp != 0) {
   2902                 return cmp < 0;
   2903             }
   2904         }
   2905         return *pRight != 0;
   2906     }
   2907 };
   2908 
   2909 /**
   2910  * Null conversion class for MBCS/UTF-8 to char (or equivalent).
   2911  */
   2912 template<class SI_CHAR>
   2913 class SI_ConvertA {
   2914     bool m_bStoreIsUtf8;
   2915 protected:
   2916     SI_ConvertA() { }
   2917 public:
   2918     SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
   2919 
   2920     /* copy and assignment */
   2921     SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }
   2922     SI_ConvertA & operator=(const SI_ConvertA & rhs) {
   2923         m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
   2924         return *this;
   2925     }
   2926 
   2927     /** Calculate the number of SI_CHAR required for converting the input
   2928      * from the storage format. The storage format is always UTF-8 or MBCS.
   2929      *
   2930      * @param a_pInputData  Data in storage format to be converted to SI_CHAR.
   2931      * @param a_uInputDataLen Length of storage format data in bytes. This
   2932      *                      must be the actual length of the data, including
   2933      *                      NULL byte if NULL terminated string is required.
   2934      * @return              Number of SI_CHAR required by the string when
   2935      *                      converted. If there are embedded NULL bytes in the
   2936      *                      input data, only the string up and not including
   2937      *                      the NULL byte will be converted.
   2938      * @return              -1 cast to size_t on a conversion error.
   2939      */
   2940     size_t SizeFromStore(
   2941         const char *    a_pInputData,
   2942         size_t          a_uInputDataLen)
   2943     {
   2944         (void)a_pInputData;
   2945         SI_ASSERT(a_uInputDataLen != (size_t) -1);
   2946 
   2947         // ASCII/MBCS/UTF-8 needs no conversion
   2948         return a_uInputDataLen;
   2949     }
   2950 
   2951     /** Convert the input string from the storage format to SI_CHAR.
   2952      * The storage format is always UTF-8 or MBCS.
   2953      *
   2954      * @param a_pInputData  Data in storage format to be converted to SI_CHAR.
   2955      * @param a_uInputDataLen Length of storage format data in bytes. This
   2956      *                      must be the actual length of the data, including
   2957      *                      NULL byte if NULL terminated string is required.
   2958      * @param a_pOutputData Pointer to the output buffer to received the
   2959      *                      converted data.
   2960      * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
   2961      * @return              true if all of the input data was successfully
   2962      *                      converted.
   2963      */
   2964     bool ConvertFromStore(
   2965         const char *    a_pInputData,
   2966         size_t          a_uInputDataLen,
   2967         SI_CHAR *       a_pOutputData,
   2968         size_t          a_uOutputDataSize)
   2969     {
   2970         // ASCII/MBCS/UTF-8 needs no conversion
   2971         if (a_uInputDataLen > a_uOutputDataSize) {
   2972             return false;
   2973         }
   2974         memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
   2975         return true;
   2976     }
   2977 
   2978     /** Calculate the number of char required by the storage format of this
   2979      * data. The storage format is always UTF-8 or MBCS.
   2980      *
   2981      * @param a_pInputData  NULL terminated string to calculate the number of
   2982      *                      bytes required to be converted to storage format.
   2983      * @return              Number of bytes required by the string when
   2984      *                      converted to storage format. This size always
   2985      *                      includes space for the terminating NULL character.
   2986      * @return              -1 cast to size_t on a conversion error.
   2987      */
   2988     size_t SizeToStore(
   2989         const SI_CHAR * a_pInputData)
   2990     {
   2991         // ASCII/MBCS/UTF-8 needs no conversion
   2992         return strlen((const char *)a_pInputData) + 1;
   2993     }
   2994 
   2995     /** Convert the input string to the storage format of this data.
   2996      * The storage format is always UTF-8 or MBCS.
   2997      *
   2998      * @param a_pInputData  NULL terminated source string to convert. All of
   2999      *                      the data will be converted including the
   3000      *                      terminating NULL character.
   3001      * @param a_pOutputData Pointer to the buffer to receive the converted
   3002      *                      string.
   3003      * @param a_uOutputDataSize Size of the output buffer in char.
   3004      * @return              true if all of the input data, including the
   3005      *                      terminating NULL character was successfully
   3006      *                      converted.
   3007      */
   3008     bool ConvertToStore(
   3009         const SI_CHAR * a_pInputData,
   3010         char *          a_pOutputData,
   3011         size_t          a_uOutputDataSize)
   3012     {
   3013         // calc input string length (SI_CHAR type and size independent)
   3014         size_t uInputLen = strlen((const char *)a_pInputData) + 1;
   3015         if (uInputLen > a_uOutputDataSize) {
   3016             return false;
   3017         }
   3018 
   3019         // ascii/UTF-8 needs no conversion
   3020         memcpy(a_pOutputData, a_pInputData, uInputLen);
   3021         return true;
   3022     }
   3023 };
   3024 
   3025 
   3026 // ---------------------------------------------------------------------------
   3027 //                              SI_CONVERT_GENERIC
   3028 // ---------------------------------------------------------------------------
   3029 #ifdef SI_CONVERT_GENERIC
   3030 
   3031 #define SI_Case     SI_GenericCase
   3032 #define SI_NoCase   SI_GenericNoCase
   3033 
   3034 #include <wchar.h>
   3035 #include "ConvertUTF.h"
   3036 
   3037 /**
   3038  * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference
   3039  * library functions. This can be used on all platforms.
   3040  */
   3041 template<class SI_CHAR>
   3042 class SI_ConvertW {
   3043     bool m_bStoreIsUtf8;
   3044 protected:
   3045     SI_ConvertW() { }
   3046 public:
   3047     SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
   3048 
   3049     /* copy and assignment */
   3050     SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
   3051     SI_ConvertW & operator=(const SI_ConvertW & rhs) {
   3052         m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
   3053         return *this;
   3054     }
   3055 
   3056     /** Calculate the number of SI_CHAR required for converting the input
   3057      * from the storage format. The storage format is always UTF-8 or MBCS.
   3058      *
   3059      * @param a_pInputData  Data in storage format to be converted to SI_CHAR.
   3060      * @param a_uInputDataLen Length of storage format data in bytes. This
   3061      *                      must be the actual length of the data, including
   3062      *                      NULL byte if NULL terminated string is required.
   3063      * @return              Number of SI_CHAR required by the string when
   3064      *                      converted. If there are embedded NULL bytes in the
   3065      *                      input data, only the string up and not including
   3066      *                      the NULL byte will be converted.
   3067      * @return              -1 cast to size_t on a conversion error.
   3068      */
   3069     size_t SizeFromStore(
   3070         const char *    a_pInputData,
   3071         size_t          a_uInputDataLen)
   3072     {
   3073         SI_ASSERT(a_uInputDataLen != (size_t) -1);
   3074 
   3075         if (m_bStoreIsUtf8) {
   3076             // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
   3077             // so we just return the same number of characters required as for
   3078             // the source text.
   3079             return a_uInputDataLen;
   3080         }
   3081 
   3082 #if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux))
   3083         // fall back processing for platforms that don't support a NULL dest to mbstowcs
   3084         // worst case scenario is 1:1, this will be a sufficient buffer size
   3085         (void)a_pInputData;
   3086         return a_uInputDataLen;
   3087 #else
   3088         // get the actual required buffer size
   3089         return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
   3090 #endif
   3091     }
   3092 
   3093     /** Convert the input string from the storage format to SI_CHAR.
   3094      * The storage format is always UTF-8 or MBCS.
   3095      *
   3096      * @param a_pInputData  Data in storage format to be converted to SI_CHAR.
   3097      * @param a_uInputDataLen Length of storage format data in bytes. This
   3098      *                       must be the actual length of the data, including
   3099      *                       NULL byte if NULL terminated string is required.
   3100      * @param a_pOutputData Pointer to the output buffer to received the
   3101      *                       converted data.
   3102      * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
   3103      * @return              true if all of the input data was successfully
   3104      *                       converted.
   3105      */
   3106     bool ConvertFromStore(
   3107         const char *    a_pInputData,
   3108         size_t          a_uInputDataLen,
   3109         SI_CHAR *       a_pOutputData,
   3110         size_t          a_uOutputDataSize)
   3111     {
   3112         if (m_bStoreIsUtf8) {
   3113             // This uses the Unicode reference implementation to do the
   3114             // conversion from UTF-8 to wchar_t. The required files are
   3115             // ConvertUTF.h and ConvertUTF.c which should be included in
   3116             // the distribution but are publicly available from unicode.org
   3117             // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
   3118             ConversionResult retval;
   3119             const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
   3120             if (sizeof(wchar_t) == sizeof(UTF32)) {
   3121                 UTF32 * pUtf32 = (UTF32 *) a_pOutputData;
   3122                 retval = ConvertUTF8toUTF32(
   3123                     &pUtf8, pUtf8 + a_uInputDataLen,
   3124                     &pUtf32, pUtf32 + a_uOutputDataSize,
   3125                     lenientConversion);
   3126             }
   3127             else if (sizeof(wchar_t) == sizeof(UTF16)) {
   3128                 UTF16 * pUtf16 = (UTF16 *) a_pOutputData;
   3129                 retval = ConvertUTF8toUTF16(
   3130                     &pUtf8, pUtf8 + a_uInputDataLen,
   3131                     &pUtf16, pUtf16 + a_uOutputDataSize,
   3132                     lenientConversion);
   3133             }
   3134             return retval == conversionOK;
   3135         }
   3136 
   3137         // convert to wchar_t
   3138         size_t retval = mbstowcs(a_pOutputData,
   3139             a_pInputData, a_uOutputDataSize);
   3140         return retval != (size_t)(-1);
   3141     }
   3142 
   3143     /** Calculate the number of char required by the storage format of this
   3144      * data. The storage format is always UTF-8 or MBCS.
   3145      *
   3146      * @param a_pInputData  NULL terminated string to calculate the number of
   3147      *                       bytes required to be converted to storage format.
   3148      * @return              Number of bytes required by the string when
   3149      *                       converted to storage format. This size always
   3150      *                       includes space for the terminating NULL character.
   3151      * @return              -1 cast to size_t on a conversion error.
   3152      */
   3153     size_t SizeToStore(
   3154         const SI_CHAR * a_pInputData)
   3155     {
   3156         if (m_bStoreIsUtf8) {
   3157             // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
   3158             size_t uLen = 0;
   3159             while (a_pInputData[uLen]) {
   3160                 ++uLen;
   3161             }
   3162             return (6 * uLen) + 1;
   3163         }
   3164         else {
   3165             size_t uLen = wcstombs(NULL, a_pInputData, 0);
   3166             if (uLen == (size_t)(-1)) {
   3167                 return uLen;
   3168             }
   3169             return uLen + 1; // include NULL terminator
   3170         }
   3171     }
   3172 
   3173     /** Convert the input string to the storage format of this data.
   3174      * The storage format is always UTF-8 or MBCS.
   3175      *
   3176      * @param a_pInputData  NULL terminated source string to convert. All of
   3177      *                       the data will be converted including the
   3178      *                       terminating NULL character.
   3179      * @param a_pOutputData Pointer to the buffer to receive the converted
   3180      *                       string.
   3181      * @param a_uOutputDataSize Size of the output buffer in char.
   3182      * @return              true if all of the input data, including the
   3183      *                       terminating NULL character was successfully
   3184      *                       converted.
   3185      */
   3186     bool ConvertToStore(
   3187         const SI_CHAR * a_pInputData,
   3188         char *          a_pOutputData,
   3189         size_t          a_uOutputDataSize
   3190         )
   3191     {
   3192         if (m_bStoreIsUtf8) {
   3193             // calc input string length (SI_CHAR type and size independent)
   3194             size_t uInputLen = 0;
   3195             while (a_pInputData[uInputLen]) {
   3196                 ++uInputLen;
   3197             }
   3198             ++uInputLen; // include the NULL char
   3199 
   3200             // This uses the Unicode reference implementation to do the
   3201             // conversion from wchar_t to UTF-8. The required files are
   3202             // ConvertUTF.h and ConvertUTF.c which should be included in
   3203             // the distribution but are publicly available from unicode.org
   3204             // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
   3205             ConversionResult retval;
   3206             UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
   3207             if (sizeof(wchar_t) == sizeof(UTF32)) {
   3208                 const UTF32 * pUtf32 = (const UTF32 *) a_pInputData;
   3209                 retval = ConvertUTF32toUTF8(
   3210                     &pUtf32, pUtf32 + uInputLen,
   3211                     &pUtf8, pUtf8 + a_uOutputDataSize,
   3212                     lenientConversion);
   3213             }
   3214             else if (sizeof(wchar_t) == sizeof(UTF16)) {
   3215                 const UTF16 * pUtf16 = (const UTF16 *) a_pInputData;
   3216                 retval = ConvertUTF16toUTF8(
   3217                     &pUtf16, pUtf16 + uInputLen,
   3218                     &pUtf8, pUtf8 + a_uOutputDataSize,
   3219                     lenientConversion);
   3220             }
   3221             return retval == conversionOK;
   3222         }
   3223         else {
   3224             size_t retval = wcstombs(a_pOutputData,
   3225                 a_pInputData, a_uOutputDataSize);
   3226             return retval != (size_t) -1;
   3227         }
   3228     }
   3229 };
   3230 
   3231 #endif // SI_CONVERT_GENERIC
   3232 
   3233 
   3234 // ---------------------------------------------------------------------------
   3235 //                              SI_CONVERT_ICU
   3236 // ---------------------------------------------------------------------------
   3237 #ifdef SI_CONVERT_ICU
   3238 
   3239 #define SI_Case     SI_GenericCase
   3240 #define SI_NoCase   SI_GenericNoCase
   3241 
   3242 #include <unicode/ucnv.h>
   3243 
   3244 /**
   3245  * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms.
   3246  */
   3247 template<class SI_CHAR>
   3248 class SI_ConvertW {
   3249     const char * m_pEncoding;
   3250     UConverter * m_pConverter;
   3251 protected:
   3252     SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
   3253 public:
   3254     SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
   3255         m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
   3256     }
   3257 
   3258     /* copy and assignment */
   3259     SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
   3260     SI_ConvertW & operator=(const SI_ConvertW & rhs) {
   3261         m_pEncoding = rhs.m_pEncoding;
   3262         m_pConverter = NULL;
   3263         return *this;
   3264     }
   3265     ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); }
   3266 
   3267     /** Calculate the number of UChar required for converting the input
   3268      * from the storage format. The storage format is always UTF-8 or MBCS.
   3269      *
   3270      * @param a_pInputData  Data in storage format to be converted to UChar.
   3271      * @param a_uInputDataLen Length of storage format data in bytes. This
   3272      *                      must be the actual length of the data, including
   3273      *                      NULL byte if NULL terminated string is required.
   3274      * @return              Number of UChar required by the string when
   3275      *                      converted. If there are embedded NULL bytes in the
   3276      *                      input data, only the string up and not including
   3277      *                      the NULL byte will be converted.
   3278      * @return              -1 cast to size_t on a conversion error.
   3279      */
   3280     size_t SizeFromStore(
   3281         const char *    a_pInputData,
   3282         size_t          a_uInputDataLen)
   3283     {
   3284         SI_ASSERT(a_uInputDataLen != (size_t) -1);
   3285 
   3286         UErrorCode nError;
   3287 
   3288         if (!m_pConverter) {
   3289             nError = U_ZERO_ERROR;
   3290             m_pConverter = ucnv_open(m_pEncoding, &nError);
   3291             if (U_FAILURE(nError)) {
   3292                 return (size_t) -1;
   3293             }
   3294         }
   3295 
   3296         nError = U_ZERO_ERROR;
   3297         int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,
   3298             a_pInputData, (int32_t) a_uInputDataLen, &nError);
   3299         if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
   3300             return (size_t) -1;
   3301         }
   3302 
   3303         return (size_t) nLen;
   3304     }
   3305 
   3306     /** Convert the input string from the storage format to UChar.
   3307      * The storage format is always UTF-8 or MBCS.
   3308      *
   3309      * @param a_pInputData  Data in storage format to be converted to UChar.
   3310      * @param a_uInputDataLen Length of storage format data in bytes. This
   3311      *                      must be the actual length of the data, including
   3312      *                      NULL byte if NULL terminated string is required.
   3313      * @param a_pOutputData Pointer to the output buffer to received the
   3314      *                      converted data.
   3315      * @param a_uOutputDataSize Size of the output buffer in UChar.
   3316      * @return              true if all of the input data was successfully
   3317      *                      converted.
   3318      */
   3319     bool ConvertFromStore(
   3320         const char *    a_pInputData,
   3321         size_t          a_uInputDataLen,
   3322         UChar *         a_pOutputData,
   3323         size_t          a_uOutputDataSize)
   3324     {
   3325         UErrorCode nError;
   3326 
   3327         if (!m_pConverter) {
   3328             nError = U_ZERO_ERROR;
   3329             m_pConverter = ucnv_open(m_pEncoding, &nError);
   3330             if (U_FAILURE(nError)) {
   3331                 return false;
   3332             }
   3333         }
   3334 
   3335         nError = U_ZERO_ERROR;
   3336         ucnv_toUChars(m_pConverter,
   3337             a_pOutputData, (int32_t) a_uOutputDataSize,
   3338             a_pInputData, (int32_t) a_uInputDataLen, &nError);
   3339         if (U_FAILURE(nError)) {
   3340             return false;
   3341         }
   3342 
   3343         return true;
   3344     }
   3345 
   3346     /** Calculate the number of char required by the storage format of this
   3347      * data. The storage format is always UTF-8 or MBCS.
   3348      *
   3349      * @param a_pInputData  NULL terminated string to calculate the number of
   3350      *                      bytes required to be converted to storage format.
   3351      * @return              Number of bytes required by the string when
   3352      *                      converted to storage format. This size always
   3353      *                      includes space for the terminating NULL character.
   3354      * @return              -1 cast to size_t on a conversion error.
   3355      */
   3356     size_t SizeToStore(
   3357         const UChar * a_pInputData)
   3358     {
   3359         UErrorCode nError;
   3360 
   3361         if (!m_pConverter) {
   3362             nError = U_ZERO_ERROR;
   3363             m_pConverter = ucnv_open(m_pEncoding, &nError);
   3364             if (U_FAILURE(nError)) {
   3365                 return (size_t) -1;
   3366             }
   3367         }
   3368 
   3369         nError = U_ZERO_ERROR;
   3370         int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,
   3371             a_pInputData, -1, &nError);
   3372         if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
   3373             return (size_t) -1;
   3374         }
   3375 
   3376         return (size_t) nLen + 1;
   3377     }
   3378 
   3379     /** Convert the input string to the storage format of this data.
   3380      * The storage format is always UTF-8 or MBCS.
   3381      *
   3382      * @param a_pInputData  NULL terminated source string to convert. All of
   3383      *                      the data will be converted including the
   3384      *                      terminating NULL character.
   3385      * @param a_pOutputData Pointer to the buffer to receive the converted
   3386      *                      string.
   3387      * @param a_pOutputDataSize Size of the output buffer in char.
   3388      * @return              true if all of the input data, including the
   3389      *                      terminating NULL character was successfully
   3390      *                      converted.
   3391      */
   3392     bool ConvertToStore(
   3393         const UChar *   a_pInputData,
   3394         char *          a_pOutputData,
   3395         size_t          a_uOutputDataSize)
   3396     {
   3397         UErrorCode nError;
   3398 
   3399         if (!m_pConverter) {
   3400             nError = U_ZERO_ERROR;
   3401             m_pConverter = ucnv_open(m_pEncoding, &nError);
   3402             if (U_FAILURE(nError)) {
   3403                 return false;
   3404             }
   3405         }
   3406 
   3407         nError = U_ZERO_ERROR;
   3408         ucnv_fromUChars(m_pConverter,
   3409             a_pOutputData, (int32_t) a_uOutputDataSize,
   3410             a_pInputData, -1, &nError);
   3411         if (U_FAILURE(nError)) {
   3412             return false;
   3413         }
   3414 
   3415         return true;
   3416     }
   3417 };
   3418 
   3419 #endif // SI_CONVERT_ICU
   3420 
   3421 
   3422 // ---------------------------------------------------------------------------
   3423 //                              SI_CONVERT_WIN32
   3424 // ---------------------------------------------------------------------------
   3425 #ifdef SI_CONVERT_WIN32
   3426 
   3427 #define SI_Case     SI_GenericCase
   3428 
   3429 // Windows CE doesn't have errno or MBCS libraries
   3430 #ifdef _WIN32_WCE
   3431 # ifndef SI_NO_MBCS
   3432 #  define SI_NO_MBCS
   3433 # endif
   3434 #endif
   3435 
   3436 #include <windows.h>
   3437 #ifdef SI_NO_MBCS
   3438 # define SI_NoCase   SI_GenericNoCase
   3439 #else // !SI_NO_MBCS
   3440 /**
   3441  * Case-insensitive comparison class using Win32 MBCS functions. This class
   3442  * returns a case-insensitive semi-collation order for MBCS text. It may not
   3443  * be safe for UTF-8 text returned in char format as we don't know what
   3444  * characters will be folded by the function! Therefore, if you are using
   3445  * SI_CHAR == char and SetUnicode(true), then you need to use the generic
   3446  * SI_NoCase class instead.
   3447  */
   3448 #include <mbstring.h>
   3449 template<class SI_CHAR>
   3450 struct SI_NoCase {
   3451     bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
   3452         if (sizeof(SI_CHAR) == sizeof(char)) {
   3453             return _mbsicmp((const unsigned char *)pLeft,
   3454                 (const unsigned char *)pRight) < 0;
   3455         }
   3456         if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
   3457             return _wcsicmp((const wchar_t *)pLeft,
   3458                 (const wchar_t *)pRight) < 0;
   3459         }
   3460         return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight);
   3461     }
   3462 };
   3463 #endif // SI_NO_MBCS
   3464 
   3465 /**
   3466  * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses
   3467  * only the Win32 functions and doesn't require the external Unicode UTF-8
   3468  * conversion library. It will not work on Windows 95 without using Microsoft
   3469  * Layer for Unicode in your application.
   3470  */
   3471 template<class SI_CHAR>
   3472 class SI_ConvertW {
   3473     UINT m_uCodePage;
   3474 protected:
   3475     SI_ConvertW() { }
   3476 public:
   3477     SI_ConvertW(bool a_bStoreIsUtf8) {
   3478         m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP;
   3479     }
   3480 
   3481     /* copy and assignment */
   3482     SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
   3483     SI_ConvertW & operator=(const SI_ConvertW & rhs) {
   3484         m_uCodePage = rhs.m_uCodePage;
   3485         return *this;
   3486     }
   3487 
   3488     /** Calculate the number of SI_CHAR required for converting the input
   3489      * from the storage format. The storage format is always UTF-8 or MBCS.
   3490      *
   3491      * @param a_pInputData  Data in storage format to be converted to SI_CHAR.
   3492      * @param a_uInputDataLen Length of storage format data in bytes. This
   3493      *                      must be the actual length of the data, including
   3494      *                      NULL byte if NULL terminated string is required.
   3495      * @return              Number of SI_CHAR required by the string when
   3496      *                      converted. If there are embedded NULL bytes in the
   3497      *                      input data, only the string up and not including
   3498      *                      the NULL byte will be converted.
   3499      * @return              -1 cast to size_t on a conversion error.
   3500      */
   3501     size_t SizeFromStore(
   3502         const char *    a_pInputData,
   3503         size_t          a_uInputDataLen)
   3504     {
   3505         SI_ASSERT(a_uInputDataLen != (size_t) -1);
   3506 
   3507         int retval = MultiByteToWideChar(
   3508             m_uCodePage, 0,
   3509             a_pInputData, (int) a_uInputDataLen,
   3510             0, 0);
   3511         return (size_t)(retval > 0 ? retval : -1);
   3512     }
   3513 
   3514     /** Convert the input string from the storage format to SI_CHAR.
   3515      * The storage format is always UTF-8 or MBCS.
   3516      *
   3517      * @param a_pInputData  Data in storage format to be converted to SI_CHAR.
   3518      * @param a_uInputDataLen Length of storage format data in bytes. This
   3519      *                      must be the actual length of the data, including
   3520      *                      NULL byte if NULL terminated string is required.
   3521      * @param a_pOutputData Pointer to the output buffer to received the
   3522      *                      converted data.
   3523      * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
   3524      * @return              true if all of the input data was successfully
   3525      *                      converted.
   3526      */
   3527     bool ConvertFromStore(
   3528         const char *    a_pInputData,
   3529         size_t          a_uInputDataLen,
   3530         SI_CHAR *       a_pOutputData,
   3531         size_t          a_uOutputDataSize)
   3532     {
   3533         int nSize = MultiByteToWideChar(
   3534             m_uCodePage, 0,
   3535             a_pInputData, (int) a_uInputDataLen,
   3536             (wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
   3537         return (nSize > 0);
   3538     }
   3539 
   3540     /** Calculate the number of char required by the storage format of this
   3541      * data. The storage format is always UTF-8.
   3542      *
   3543      * @param a_pInputData  NULL terminated string to calculate the number of
   3544      *                      bytes required to be converted to storage format.
   3545      * @return              Number of bytes required by the string when
   3546      *                      converted to storage format. This size always
   3547      *                      includes space for the terminating NULL character.
   3548      * @return              -1 cast to size_t on a conversion error.
   3549      */
   3550     size_t SizeToStore(
   3551         const SI_CHAR * a_pInputData)
   3552     {
   3553         int retval = WideCharToMultiByte(
   3554             m_uCodePage, 0,
   3555             (const wchar_t *) a_pInputData, -1,
   3556             0, 0, 0, 0);
   3557         return (size_t) (retval > 0 ? retval : -1);
   3558     }
   3559 
   3560     /** Convert the input string to the storage format of this data.
   3561      * The storage format is always UTF-8 or MBCS.
   3562      *
   3563      * @param a_pInputData  NULL terminated source string to convert. All of
   3564      *                      the data will be converted including the
   3565      *                      terminating NULL character.
   3566      * @param a_pOutputData Pointer to the buffer to receive the converted
   3567      *                      string.
   3568      * @param a_pOutputDataSize Size of the output buffer in char.
   3569      * @return              true if all of the input data, including the
   3570      *                      terminating NULL character was successfully
   3571      *                      converted.
   3572      */
   3573     bool ConvertToStore(
   3574         const SI_CHAR * a_pInputData,
   3575         char *          a_pOutputData,
   3576         size_t          a_uOutputDataSize)
   3577     {
   3578         int retval = WideCharToMultiByte(
   3579             m_uCodePage, 0,
   3580             (const wchar_t *) a_pInputData, -1,
   3581             a_pOutputData, (int) a_uOutputDataSize, 0, 0);
   3582         return retval > 0;
   3583     }
   3584 };
   3585 
   3586 #endif // SI_CONVERT_WIN32
   3587 
   3588 
   3589 
   3590 // ---------------------------------------------------------------------------
   3591 //                              SI_NO_CONVERSION
   3592 // ---------------------------------------------------------------------------
   3593 #ifdef SI_NO_CONVERSION
   3594 
   3595 #define SI_Case     SI_GenericCase
   3596 #define SI_NoCase   SI_GenericNoCase
   3597 
   3598 #endif // SI_NO_CONVERSION
   3599 
   3600 
   3601 
   3602 // ---------------------------------------------------------------------------
   3603 //                                  TYPE DEFINITIONS
   3604 // ---------------------------------------------------------------------------
   3605 
   3606 typedef CSimpleIniTempl<char,
   3607     SI_NoCase<char>,SI_ConvertA<char> >                 CSimpleIniA;
   3608 typedef CSimpleIniTempl<char,
   3609     SI_Case<char>,SI_ConvertA<char> >                   CSimpleIniCaseA;
   3610 
   3611 #if defined(SI_NO_CONVERSION)
   3612 // if there is no wide char conversion then we don't need to define the 
   3613 // widechar "W" versions of CSimpleIni
   3614 # define CSimpleIni      CSimpleIniA
   3615 # define CSimpleIniCase  CSimpleIniCaseA
   3616 # define SI_NEWLINE      SI_NEWLINE_A
   3617 #else
   3618 # if defined(SI_CONVERT_ICU)
   3619 typedef CSimpleIniTempl<UChar,
   3620     SI_NoCase<UChar>,SI_ConvertW<UChar> >               CSimpleIniW;
   3621 typedef CSimpleIniTempl<UChar,
   3622     SI_Case<UChar>,SI_ConvertW<UChar> >                 CSimpleIniCaseW;
   3623 # else
   3624 typedef CSimpleIniTempl<wchar_t,
   3625     SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> >           CSimpleIniW;
   3626 typedef CSimpleIniTempl<wchar_t,
   3627     SI_Case<wchar_t>,SI_ConvertW<wchar_t> >             CSimpleIniCaseW;
   3628 # endif
   3629 
   3630 # ifdef _UNICODE
   3631 #  define CSimpleIni      CSimpleIniW
   3632 #  define CSimpleIniCase  CSimpleIniCaseW
   3633 #  define SI_NEWLINE      SI_NEWLINE_W
   3634 # else // !_UNICODE 
   3635 #  define CSimpleIni      CSimpleIniA
   3636 #  define CSimpleIniCase  CSimpleIniCaseA
   3637 #  define SI_NEWLINE      SI_NEWLINE_A
   3638 # endif // _UNICODE
   3639 #endif
   3640 
   3641 #ifdef _MSC_VER
   3642 # pragma warning (pop)
   3643 #endif
   3644 
   3645 #endif // INCLUDED_SimpleIni_h
   3646