source: rtems-tools/rtemstoolkit/SimpleIni.h @ 3618a62

Last change on this file since 3618a62 was 87e0e76, checked in by Chris Johns <chrisj@…>, on Sep 13, 2014 at 2:09:16 AM

Refactor code into the RTEMS Toolkit.

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