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