source: rtems-tools/rtemstoolkit/SimpleIni.h

Last change on this file was 00af5a6, checked in by Chris Johns <chrisj@…>, on 11/22/23 at 03:30:59

rtemstoolkit: Update SimpleIni? to latest

The resolves C++17 warnings.

Updates #4970

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