source: nscp/include/simpleini/simpleini.h @ 3eedfa6

0.4.00.4.10.4.2
Last change on this file since 3eedfa6 was 3eedfa6, checked in by Michael Medin <michael@…>, 4 years ago

fixed so we have a settings subsytem (ini files) which work on linux

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