4 <tr><th>Library <td>SimpleIni
5 <tr><th>File <td>SimpleIni.h
6 <tr><th>Author <td>Brodie Thiesfield [code at jellycan dot com]
7 <tr><th>Source <td>http://code.jellycan.com/simpleini/
8 <tr><th>Version <td>4.5
11 Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation.
13 @section intro INTRODUCTION
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.
20 @section features FEATURES
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)
48 @section usage USAGE SUMMARY
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
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.
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
77 #1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.<br>
78 #2 Only affects Windows. On Windows this uses MBCS functions and
79 so may fold case incorrectly leading to uncertain results.
80 -# Call Load() or LoadFile() to load and parse the INI configuration file
81 -# Access and modify the data of the file using the following functions
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
92 -# Call Save() or SaveFile() to save the INI configuration data
94 @section iostreams IO STREAMS
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
102 @section multiline MULTI-LINE VALUES
104 Values that span multiple lines are created using the following format.
108 .... multiline value ....
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
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
124 @section comments COMMENTS
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
130 - Every section may have a single "section comment". It will start
131 with the first comment line following the file comment, or the last
132 data entry. It ends at the beginning of the section.
133 - Every key may have a single "key comment". This comment will start
134 with the first comment line following the section start, or the file
135 comment if there is no section name.
136 - MultiKey entries may have only a single comment and will take the
137 comment associated with the first key found.
138 - Comments are set at the time that the file, section or key is first
139 created. The only way to modify a comment on a section or a key is to
140 delete that entry and recreate it with the new comment. There is no
141 way to change the file comment.
143 @section save SAVE ORDER
145 The sections and keys are written out in the same order as they were
146 read in from the file. Sections and keys added to the data after the
147 file has been loaded will be added to the end of the file when it is
148 written. There is no way to specify the location of a section or key
149 other than in first-created, first-saved order.
153 - To load UTF-8 data on Windows 95, you need to use Microsoft Layer for
154 Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU.
155 - When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked.
156 - When using SI_CONVERT_ICU, ICU header files must be on the include
157 path and icuuc.lib must be linked in.
158 - To load a UTF-8 file on Windows AND expose it with SI_CHAR == char,
159 you should use SI_CONVERT_GENERIC.
160 - The collation (sorting) order used for sections and keys returned from
161 iterators is NOT DEFINED. If collation order of the text is important
162 then it should be done yourself by either supplying a replacement
163 SI_STRLESS class, or by sorting the strings external to this library.
164 - Usage of the <mbstring.h> header on Windows can be disabled by defining
165 SI_NO_MBCS. This is defined automatically on Windows CE platforms.
168 @section licence MIT LICENCE
170 The licence text below is the boilerplate "MIT Licence" used from:
171 http://www.opensource.org/licenses/mit-license.php
173 Copyright (c) 2006, Brodie Thiesfield
175 Permission is hereby granted, free of charge, to any person obtaining a copy
176 of this software and associated documentation files (the "Software"), to deal
177 in the Software without restriction, including without limitation the rights
178 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
179 copies of the Software, and to permit persons to whom the Software is furnished
180 to do so, subject to the following conditions:
182 The above copyright notice and this permission notice shall be included in
183 all copies or substantial portions of the Software.
185 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
186 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
187 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
188 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
189 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
190 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
193 #ifndef INCLUDED_SimpleIni_h
194 #define INCLUDED_SimpleIni_h
196 #if defined(_MSC_VER) && (_MSC_VER >= 1020)
200 // Disable these warnings in MSVC:
201 // 4127 "conditional expression is constant" as the conversion classes trigger
202 // it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
203 // be optimized away in a release build.
204 // 4503 'insert' : decorated name length exceeded, name was truncated
205 // 4702 "unreachable code" as the MS STL header causes it in release mode.
206 // Again, the code causing the warning will be cleaned up by the compiler.
207 // 4786 "identifier truncated to 256 characters" as this is thrown hundreds
208 // of times VC6 as soon as STL is used.
210 # pragma warning (push)
211 # pragma warning (disable: 4127 4503 4702 4786)
220 #ifdef SI_SUPPORT_IOSTREAMS
222 #endif // SI_SUPPORT_IOSTREAMS
228 # define SI_ASSERT(x) assert(x)
230 # define SI_ASSERT(x)
234 SI_OK
= 0, //!< No error
235 SI_UPDATED
= 1, //!< An existing value was updated
236 SI_INSERTED
= 2, //!< A new value was inserted
238 // note: test for any error with (retval < 0)
239 SI_FAIL
= -1, //!< Generic failure
240 SI_NOMEM
= -2, //!< Out of memory error
241 SI_FILE
= -3 //!< File error (see errno for detail error)
244 #define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
247 # define SI_NEWLINE_A "\r\n"
248 # define SI_NEWLINE_W L"\r\n"
250 # define SI_NEWLINE_A "\n"
251 # define SI_NEWLINE_W L"\n"
254 #if defined(SI_CONVERT_ICU)
255 # include <unicode/ustring.h>
259 # define SI_HAS_WIDE_FILE
260 # define SI_WCHAR_T wchar_t
261 #elif defined(SI_CONVERT_ICU)
262 # define SI_HAS_WIDE_FILE
263 # define SI_WCHAR_T UChar
267 // ---------------------------------------------------------------------------
268 // MAIN TEMPLATE CLASS
269 // ---------------------------------------------------------------------------
271 /** Simple INI file reader.
273 This can be instantiated with the choice of unicode or native characterset,
274 and case sensitive or insensitive comparisons of section and key names.
275 The supported combinations are pre-defined with the following typedefs:
278 <tr><th>Interface <th>Case-sensitive <th>Typedef
279 <tr><td>char <td>No <td>CSimpleIniA
280 <tr><td>char <td>Yes <td>CSimpleIniCaseA
281 <tr><td>wchar_t <td>No <td>CSimpleIniW
282 <tr><td>wchar_t <td>Yes <td>CSimpleIniCaseW
285 Note that using other types for the SI_CHAR is supported. For instance,
286 unsigned char, unsigned short, etc. Note that where the alternative type
287 is a different size to char/wchar_t you may need to supply new helper
288 classes for SI_STRLESS and SI_CONVERTER.
290 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
291 class CSimpleIniTempl
296 const SI_CHAR
* pItem
;
297 const SI_CHAR
* pComment
;
300 Entry(const SI_CHAR
* a_pszItem
= NULL
, int a_nOrder
= 0)
305 Entry(const Entry
& rhs
) { operator=(rhs
); }
306 Entry
& operator=(const Entry
& rhs
) {
308 pComment
= rhs
.pComment
;
313 #if defined(_MSC_VER) && _MSC_VER <= 1200
314 /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */
315 bool operator<(const Entry
& rhs
) const { return LoadOrder()(*this, rhs
); }
316 bool operator>(const Entry
& rhs
) const { return LoadOrder()(rhs
, *this); }
319 /** Strict less ordering by name of key only */
320 struct KeyOrder
: std::binary_function
<Entry
, Entry
, bool> {
321 bool operator()(const Entry
& lhs
, const Entry
& rhs
) const {
322 const static SI_STRLESS isLess
= SI_STRLESS();
323 return isLess(lhs
.pItem
, rhs
.pItem
);
327 /** Strict less ordering by order, and then name of key */
328 struct LoadOrder
: std::binary_function
<Entry
, Entry
, bool> {
329 bool operator()(const Entry
& lhs
, const Entry
& rhs
) const {
330 if (lhs
.nOrder
!= rhs
.nOrder
) {
331 return lhs
.nOrder
< rhs
.nOrder
;
333 return KeyOrder()(lhs
.pItem
, rhs
.pItem
);
338 /** map keys to values */
339 typedef std::multimap
<Entry
,const SI_CHAR
*,typename
Entry::KeyOrder
> TKeyVal
;
341 /** map sections to key/value map */
342 typedef std::map
<Entry
,TKeyVal
,typename
Entry::KeyOrder
> TSection
;
344 /** set of dependent string pointers. Note that these pointers are
345 dependent on memory owned by CSimpleIni.
347 typedef std::list
<Entry
> TNamesDepend
;
349 /** interface definition for the OutputWriter object to pass to Save()
350 in order to output the INI file data.
355 virtual ~OutputWriter() { }
356 virtual void Write(const char * a_pBuf
) = 0;
358 OutputWriter(const OutputWriter
&); // disable
359 OutputWriter
& operator=(const OutputWriter
&); // disable
362 /** OutputWriter class to write the INI data to a file */
363 class FileWriter
: public OutputWriter
{
366 FileWriter(FILE * a_file
) : m_file(a_file
) { }
367 void Write(const char * a_pBuf
) {
368 fputs(a_pBuf
, m_file
);
371 FileWriter(const FileWriter
&); // disable
372 FileWriter
& operator=(const FileWriter
&); // disable
375 /** OutputWriter class to write the INI data to a string */
376 class StringWriter
: public OutputWriter
{
377 std::string
& m_string
;
379 StringWriter(std::string
& a_string
) : m_string(a_string
) { }
380 void Write(const char * a_pBuf
) {
381 m_string
.append(a_pBuf
);
384 StringWriter(const StringWriter
&); // disable
385 StringWriter
& operator=(const StringWriter
&); // disable
388 #ifdef SI_SUPPORT_IOSTREAMS
389 /** OutputWriter class to write the INI data to an ostream */
390 class StreamWriter
: public OutputWriter
{
391 std::ostream
& m_ostream
;
393 StreamWriter(std::ostream
& a_ostream
) : m_ostream(a_ostream
) { }
394 void Write(const char * a_pBuf
) {
398 StreamWriter(const StreamWriter
&); // disable
399 StreamWriter
& operator=(const StreamWriter
&); // disable
401 #endif // SI_SUPPORT_IOSTREAMS
403 /** Characterset conversion utility class to convert strings to the
404 same format as is used for the storage.
406 class Converter
: private SI_CONVERTER
{
408 Converter(bool a_bStoreIsUtf8
) : SI_CONVERTER(a_bStoreIsUtf8
) {
409 m_scratch
.resize(1024);
411 Converter(const Converter
& rhs
) { operator=(rhs
); }
412 Converter
& operator=(const Converter
& rhs
) {
413 m_scratch
= rhs
.m_scratch
;
416 bool ConvertToStore(const SI_CHAR
* a_pszString
) {
417 size_t uLen
= SizeToStore(a_pszString
);
418 if (uLen
== (size_t)(-1)) {
421 while (uLen
> m_scratch
.size()) {
422 m_scratch
.resize(m_scratch
.size() * 2);
424 return SI_CONVERTER::ConvertToStore(
426 const_cast<char*>(m_scratch
.data()),
429 const char * Data() { return m_scratch
.data(); }
431 std::string m_scratch
;
435 /*-----------------------------------------------------------------------*/
437 /** Default constructor.
439 @param a_bIsUtf8 See the method SetUnicode() for details.
440 @param a_bMultiKey See the method SetMultiKey() for details.
441 @param a_bMultiLine See the method SetMultiLine() for details.
444 bool a_bIsUtf8
= false,
445 bool a_bMultiKey
= false,
446 bool a_bMultiLine
= false
452 /** Deallocate all memory stored by this object */
455 /*-----------------------------------------------------------------------*/
456 /** @{ @name Settings */
458 /** Set the storage format of the INI data. This affects both the loading
459 and saving of the INI data using all of the Load/Save API functions.
460 This value cannot be changed after any INI data has been loaded.
462 If the file is not set to Unicode (UTF-8), then the data encoding is
463 assumed to be the OS native encoding. This encoding is the system
464 locale on Linux/Unix and the legacy MBCS encoding on Windows NT/2K/XP.
465 If the storage format is set to Unicode then the file will be loaded
466 as UTF-8 encoded data regardless of the native file encoding. If
467 SI_CHAR == char then all of the char* parameters take and return UTF-8
468 encoded data regardless of the system locale.
470 \param a_bIsUtf8 Assume UTF-8 encoding for the source?
472 void SetUnicode(bool a_bIsUtf8
= true) {
473 if (!m_pData
) m_bStoreIsUtf8
= a_bIsUtf8
;
476 /** Get the storage format of the INI data. */
477 bool IsUnicode() const { return m_bStoreIsUtf8
; }
479 /** Should multiple identical keys be permitted in the file. If set to false
480 then the last value encountered will be used as the value of the key.
481 If set to true, then all values will be available to be queried. For
482 example, with the following input:
490 Then with SetMultiKey(true), both of the values "value1" and "value2"
491 will be returned for the key test. If SetMultiKey(false) is used, then
492 the value for "test" will only be "value2". This value may be changed
495 \param a_bAllowMultiKey Allow multi-keys in the source?
497 void SetMultiKey(bool a_bAllowMultiKey
= true) {
498 m_bAllowMultiKey
= a_bAllowMultiKey
;
501 /** Get the storage format of the INI data. */
502 bool IsMultiKey() const { return m_bAllowMultiKey
; }
504 /** Should data values be permitted to span multiple lines in the file. If
505 set to false then the multi-line construct <<<TAG as a value will be
506 returned as is instead of loading the data. This value may be changed
509 \param a_bAllowMultiLine Allow multi-line values in the source?
511 void SetMultiLine(bool a_bAllowMultiLine
= true) {
512 m_bAllowMultiLine
= a_bAllowMultiLine
;
515 /** Query the status of multi-line data */
516 bool IsMultiLine() const { return m_bAllowMultiLine
; }
518 /*-----------------------------------------------------------------------*/
520 @{ @name Loading INI Data */
522 /** Load an INI file from disk into memory
524 @param a_pszFile Path of the file to be loaded. This will be passed
525 to fopen() and so must be a valid path for the
528 @return SI_Error See error definitions
531 const char * a_pszFile
534 #ifdef SI_HAS_WIDE_FILE
535 /** Load an INI file from disk into memory
537 @param a_pwszFile Path of the file to be loaded in UTF-16.
539 @return SI_Error See error definitions
542 const SI_WCHAR_T
* a_pwszFile
544 #endif // SI_HAS_WIDE_FILE
546 /** Load the file from a file pointer.
548 @param a_fpFile Valid file pointer to read the file data from. The
549 file will be read until end of file.
551 @return SI_Error See error definitions
557 #ifdef SI_SUPPORT_IOSTREAMS
558 /** Load INI file data from an istream.
560 @param a_istream Stream to read from
562 @return SI_Error See error definitions
565 std::istream
& a_istream
567 #endif // SI_SUPPORT_IOSTREAMS
569 /** Load INI file data direct from a std::string
571 @param a_strData Data to be loaded
573 @return SI_Error See error definitions
575 SI_Error
Load(const std::string
& a_strData
) {
576 return Load(a_strData
.c_str(), a_strData
.size());
579 /** Load INI file data direct from memory
581 @param a_pData Data to be loaded
582 @param a_uDataLen Length of the data in bytes
584 @return SI_Error See error definitions
587 const char * a_pData
,
591 /*-----------------------------------------------------------------------*/
593 @{ @name Saving INI Data */
595 /** Save an INI file from memory to disk
597 @param a_pszFile Path of the file to be saved. This will be passed
598 to fopen() and so must be a valid path for the
601 @param a_bAddSignature Prepend the UTF-8 BOM if the output data is
602 in UTF-8 format. If it is not UTF-8 then
603 this parameter is ignored.
605 @return SI_Error See error definitions
608 const char * a_pszFile
,
609 bool a_bAddSignature
= true
612 #ifdef SI_HAS_WIDE_FILE
613 /** Save an INI file from memory to disk
615 @param a_pwszFile Path of the file to be saved in UTF-16.
617 @param a_bAddSignature Prepend the UTF-8 BOM if the output data is
618 in UTF-8 format. If it is not UTF-8 then
619 this parameter is ignored.
621 @return SI_Error See error definitions
624 const SI_WCHAR_T
* a_pwszFile
,
625 bool a_bAddSignature
= true
629 /** Save the INI data to a file. See Save() for details.
631 @param a_pFile Handle to a file. File should be opened for
634 @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
635 UTF-8 format. If it is not UTF-8 then this value is
636 ignored. Do not set this to true if anything has
637 already been written to the file.
639 @return SI_Error See error definitions
643 bool a_bAddSignature
= false
646 /** Save the INI data. The data will be written to the output device
647 in a format appropriate to the current data, selected by:
650 <tr><th>SI_CHAR <th>FORMAT
651 <tr><td>char <td>same format as when loaded (MBCS or UTF-8)
652 <tr><td>wchar_t <td>UTF-8
653 <tr><td>other <td>UTF-8
656 Note that comments from the original data is preserved as per the
657 documentation on comments. The order of the sections and values
658 from the original file will be preserved.
660 Any data prepended or appended to the output device must use the the
661 same format (MBCS or UTF-8). You may use the GetConverter() method to
662 convert text to the correct format regardless of the output format
663 being used by SimpleIni.
665 To add a BOM to UTF-8 data, write it out manually at the very beginning
666 like is done in SaveFile when a_bUseBOM is true.
668 @param a_oOutput Output writer to write the data to.
670 @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
671 UTF-8 format. If it is not UTF-8 then this value is
672 ignored. Do not set this to true if anything has
673 already been written to the OutputWriter.
675 @return SI_Error See error definitions
678 OutputWriter
& a_oOutput
,
679 bool a_bAddSignature
= false
682 #ifdef SI_SUPPORT_IOSTREAMS
683 /** Save the INI data to an ostream. See Save() for details.
685 @param a_ostream String to have the INI data appended to.
687 @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
688 UTF-8 format. If it is not UTF-8 then this value is
689 ignored. Do not set this to true if anything has
690 already been written to the stream.
692 @return SI_Error See error definitions
695 std::ostream
& a_ostream
,
696 bool a_bAddSignature
= false
699 StreamWriter
writer(a_ostream
);
700 return Save(writer
, a_bAddSignature
);
702 #endif // SI_SUPPORT_IOSTREAMS
704 /** Append the INI data to a string. See Save() for details.
706 @param a_sBuffer String to have the INI data appended to.
708 @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
709 UTF-8 format. If it is not UTF-8 then this value is
710 ignored. Do not set this to true if anything has
711 already been written to the string.
713 @return SI_Error See error definitions
716 std::string
& a_sBuffer
,
717 bool a_bAddSignature
= false
720 StringWriter
writer(a_sBuffer
);
721 return Save(writer
, a_bAddSignature
);
724 /*-----------------------------------------------------------------------*/
726 @{ @name Accessing INI Data */
728 /** Retrieve all section names. The list is returned as an STL vector of
729 names and can be iterated or searched as necessary. Note that the
730 collation order of the returned strings is NOT DEFINED.
732 NOTE! This structure contains only pointers to strings. The actual
733 string data is stored in memory owned by CSimpleIni. Ensure that the
734 CSimpleIni object is not destroyed or Reset() while these pointers
737 @param a_names Vector that will receive all of the section
738 names. See note above!
741 TNamesDepend
& a_names
744 /** Retrieve all unique key names in a section. The collation order of the
745 returned strings is NOT DEFINED. Only unique key names are returned.
747 NOTE! This structure contains only pointers to strings. The actual
748 string data is stored in memory owned by CSimpleIni. Ensure that the
749 CSimpleIni object is not destroyed or Reset() while these strings
752 @param a_pSection Section to request data for
753 @param a_names List that will receive all of the key
754 names. See note above!
756 @return true Section was found.
757 @return false Matching section was not found.
760 const SI_CHAR
* a_pSection
,
761 TNamesDepend
& a_names
764 /** Retrieve all values for a specific key. This method can be used when
765 multiple keys are both enabled and disabled.
767 NOTE! The returned values are pointers to string data stored in memory
768 owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
769 or Reset while you are using this pointer!
771 @param a_pSection Section to search
772 @param a_pKey Key to search for
773 @param a_values List to return if the key is not found
775 @return true Key was found.
776 @return false Matching section/key was not found.
779 const SI_CHAR
* a_pSection
,
780 const SI_CHAR
* a_pKey
,
781 TNamesDepend
& a_values
784 /** Query the number of keys in a specific section. Note that if multiple
785 keys are enabled, then this value may be different to the number of
786 keys returned by GetAllKeys.
788 @param a_pSection Section to request data for
790 @return -1 Section does not exist in the file
791 @return >=0 Number of keys in the section
794 const SI_CHAR
* a_pSection
797 /** Retrieve all key and value pairs for a section. The data is returned
798 as a pointer to an STL map and can be iterated or searched as
799 desired. Note that multiple entries for the same key may exist when
800 multiple keys have been enabled.
802 NOTE! This structure contains only pointers to strings. The actual
803 string data is stored in memory owned by CSimpleIni. Ensure that the
804 CSimpleIni object is not destroyed or Reset() while these strings
807 @param a_pSection Name of the section to return
808 @return boolean Was a section matching the supplied
811 const TKeyVal
* GetSection(
812 const SI_CHAR
* a_pSection
815 /** Retrieve the value for a specific key. If multiple keys are enabled
816 (see SetMultiKey) then only the first value associated with that key
817 will be returned, see GetAllValues for getting all values with multikey.
819 NOTE! The returned value is a pointer to string data stored in memory
820 owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
821 or Reset while you are using this pointer!
823 @param a_pSection Section to search
824 @param a_pKey Key to search for
825 @param a_pDefault Value to return if the key is not found
826 @param a_pHasMultiple Optionally receive notification of if there are
827 multiple entries for this key.
829 @return a_pDefault Key was not found in the section
830 @return other Value of the key
832 const SI_CHAR
* GetValue(
833 const SI_CHAR
* a_pSection
,
834 const SI_CHAR
* a_pKey
,
835 const SI_CHAR
* a_pDefault
= NULL
,
836 bool * a_pHasMultiple
= NULL
839 /** Add or update a section or value. This will always insert
840 when multiple keys are enabled.
842 @param a_pSection Section to add or update
843 @param a_pKey Key to add or update. Set to NULL to
844 create an empty section.
845 @param a_pValue Value to set. Set to NULL to create an
847 @param a_pComment Comment to be associated with the section or the
848 key. If a_pKey is NULL then it will be associated
849 with the section, otherwise the key. Note that a
850 comment may be set ONLY when the section or key is
851 first created (i.e. when this function returns the
852 value SI_INSERTED). If you wish to create a section
853 with a comment then you need to create the section
854 separately to the key. The comment string must be
855 in full comment form already (have a comment
856 character starting every line).
858 @return SI_Error See error definitions
859 @return SI_UPDATED Value was updated
860 @return SI_INSERTED Value was inserted
863 const SI_CHAR
* a_pSection
,
864 const SI_CHAR
* a_pKey
,
865 const SI_CHAR
* a_pValue
,
866 const SI_CHAR
* a_pComment
= NULL
869 return AddEntry(a_pSection
, a_pKey
, a_pValue
, a_pComment
, true);
872 /** Delete an entire section, or a key from a section. Note that the
873 data returned by GetSection is invalid and must not be used after
874 anything has been deleted from that section using this method.
875 Note when multiple keys is enabled, this will delete all keys with
876 that name; there is no way to selectively delete individual key/values
879 @param a_pSection Section to delete key from, or if
880 a_pKey is NULL, the section to remove.
881 @param a_pKey Key to remove from the section. Set to
882 NULL to remove the entire section.
883 @param a_bRemoveEmpty If the section is empty after this key has
884 been deleted, should the empty section be
887 @return true Key or section was deleted.
888 @return false Key or section was not found.
891 const SI_CHAR
* a_pSection
,
892 const SI_CHAR
* a_pKey
,
893 bool a_bRemoveEmpty
= false
896 /*-----------------------------------------------------------------------*/
898 @{ @name Converter */
900 /** Return a conversion object to convert text to the same encoding
901 as is used by the Save(), SaveFile() and SaveString() functions.
902 Use this to prepare the strings that you wish to append or prepend
903 to the output INI data.
905 Converter
GetConverter() const {
906 return Converter(m_bStoreIsUtf8
);
909 /*-----------------------------------------------------------------------*/
913 /** Parse the data looking for a file comment and store it if found.
915 SI_Error
FindFileComment(
920 /** Parse the data looking for the next valid entry. The memory pointed to
921 by a_pData is modified by inserting NULL characters. The pointer is
922 updated to the current location in the block of text.
926 const SI_CHAR
*& a_pSection
,
927 const SI_CHAR
*& a_pKey
,
928 const SI_CHAR
*& a_pVal
,
929 const SI_CHAR
*& a_pComment
932 /** Add the section/key/value to our data.
934 @param a_pSection Section name. Sections will be created if they
936 @param a_pKey Key name. May be NULL to create an empty section.
937 Existing entries will be updated. New entries will
939 @param a_pValue Value for the key.
940 @param a_pComment Comment to be associated with the section or the
941 key. If a_pKey is NULL then it will be associated
942 with the section, otherwise the key. This must be
943 a string in full comment form already (have a
944 comment character starting every line).
945 @param a_bCopyStrings Should copies of the strings be made or not.
946 If false then the pointers will be used as is.
949 const SI_CHAR
* a_pSection
,
950 const SI_CHAR
* a_pKey
,
951 const SI_CHAR
* a_pValue
,
952 const SI_CHAR
* a_pComment
,
956 /** Is the supplied character a whitespace character? */
957 inline bool IsSpace(SI_CHAR ch
) const {
958 return (ch
== ' ' || ch
== '\t' || ch
== '\r' || ch
== '\n');
961 /** Does the supplied character start a comment line? */
962 inline bool IsComment(SI_CHAR ch
) const {
963 return (ch
== ';' || ch
== '#');
967 /** Skip over a newline character (or characters) for either DOS or UNIX */
968 inline void SkipNewLine(SI_CHAR
*& a_pData
) const {
969 a_pData
+= (*a_pData
== '\r' && *(a_pData
+1) == '\n') ? 2 : 1;
972 /** Make a copy of the supplied string, replacing the original pointer */
973 SI_Error
CopyString(const SI_CHAR
*& a_pString
);
975 /** Delete a string from the copied strings buffer if necessary */
976 void DeleteString(const SI_CHAR
* a_pString
);
978 /** Internal use of our string comparison function */
979 bool IsLess(const SI_CHAR
* a_pLeft
, const SI_CHAR
* a_pRight
) const {
980 const static SI_STRLESS isLess
= SI_STRLESS();
981 return isLess(a_pLeft
, a_pRight
);
984 bool IsMultiLineTag(const SI_CHAR
* a_pData
) const;
985 bool IsMultiLineData(const SI_CHAR
* a_pData
) const;
986 bool LoadMultiLineText(
988 const SI_CHAR
*& a_pVal
,
989 const SI_CHAR
* a_pTagName
,
990 bool a_bAllowBlankLinesInComment
= false
992 bool IsNewLineChar(SI_CHAR a_c
) const;
994 bool OutputMultiLineText(
995 OutputWriter
& a_oOutput
,
996 Converter
& a_oConverter
,
997 const SI_CHAR
* a_pText
1001 /** Copy of the INI file data in our character format. This will be
1002 modified when parsed to have NULL characters added after all
1003 interesting string entries. All of the string pointers to sections,
1004 keys and values point into this block of memory.
1008 /** Length of the data that we have stored. Used when deleting strings
1009 to determine if the string is stored here or in the allocated string
1014 /** File comment for this data, if one exists. */
1015 const SI_CHAR
* m_pFileComment
;
1017 /** Parsed INI data. Section -> (Key -> Value). */
1020 /** This vector stores allocated memory for copies of strings that have
1021 been supplied after the file load. It will be empty unless SetValue()
1024 TNamesDepend m_strings
;
1026 /** Is the format of our datafile UTF-8 or MBCS? */
1027 bool m_bStoreIsUtf8
;
1029 /** Are multiple values permitted for the same key? */
1030 bool m_bAllowMultiKey
;
1032 /** Are data values permitted to span multiple lines? */
1033 bool m_bAllowMultiLine
;
1035 /** Next order value, used to ensure sections and keys are output in the
1036 same order that they are loaded/added.
1041 static std::string
str_SI_Error(SI_Error rc
)
1044 case SI_OK
: return std::string("no error");
1045 case SI_UPDATED
: return std::string("existing value updated");
1046 case SI_INSERTED
: return std::string("new value inserted");
1047 case SI_FAIL
: return std::string("generic failure");
1048 case SI_NOMEM
: return std::string("out of memory");
1049 case SI_FILE
: return std::string("file error");
1050 default: return std::string("(invalid error)");
1056 // ---------------------------------------------------------------------------
1058 // ---------------------------------------------------------------------------
1060 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1061 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::CSimpleIniTempl(
1063 bool a_bAllowMultiKey
,
1064 bool a_bAllowMultiLine
1068 , m_pFileComment(NULL
)
1069 , m_bStoreIsUtf8(a_bIsUtf8
)
1070 , m_bAllowMultiKey(a_bAllowMultiKey
)
1071 , m_bAllowMultiLine(a_bAllowMultiLine
)
1075 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1076 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::~CSimpleIniTempl()
1081 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1083 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::Reset()
1089 m_pFileComment
= NULL
;
1090 if (!m_data
.empty()) {
1091 m_data
.erase(m_data
.begin(), m_data
.end());
1094 // remove all strings
1095 if (!m_strings
.empty()) {
1096 typename
TNamesDepend::iterator i
= m_strings
.begin();
1097 for (; i
!= m_strings
.end(); ++i
) {
1098 delete[] const_cast<SI_CHAR
*>(i
->pItem
);
1100 m_strings
.erase(m_strings
.begin(), m_strings
.end());
1104 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1106 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::LoadFile(
1107 const char * a_pszFile
1111 #if __STDC_WANT_SECURE_LIB__
1112 fopen_s(&fp
, a_pszFile
, "rb");
1114 fp
= fopen(a_pszFile
, "rb");
1119 SI_Error rc
= LoadFile(fp
);
1124 #ifdef SI_HAS_WIDE_FILE
1125 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1127 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::LoadFile(
1128 const SI_WCHAR_T
* a_pwszFile
1133 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1134 _wfopen_s(&fp
, a_pwszFile
, L
"rb");
1136 fp
= _wfopen(a_pwszFile
, L
"rb");
1138 if (!fp
) return SI_FILE
;
1139 SI_Error rc
= LoadFile(fp
);
1142 #else // SI_CONVERT_ICU
1144 u_austrncpy(szFile
, a_pwszFile
, sizeof(szFile
));
1145 return LoadFile(szFile
);
1148 #endif // SI_HAS_WIDE_FILE
1150 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1152 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::LoadFile(
1156 // load the raw file data
1157 int retval
= fseek(a_fpFile
, 0, SEEK_END
);
1161 long lSize
= ftell(a_fpFile
);
1168 char * pData
= new char[lSize
];
1172 fseek(a_fpFile
, 0, SEEK_SET
);
1173 size_t uRead
= fread(pData
, sizeof(char), lSize
, a_fpFile
);
1174 if (uRead
!= (size_t) lSize
) {
1179 // convert the raw data to unicode
1180 SI_Error rc
= Load(pData
, uRead
);
1185 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1187 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::Load(
1188 const char * a_pData
,
1192 SI_CONVERTER
converter(m_bStoreIsUtf8
);
1194 if (a_uDataLen
== 0) {
1198 // consume the UTF-8 BOM if it exists
1199 if (m_bStoreIsUtf8
&& a_uDataLen
>= 3) {
1200 if (memcmp(a_pData
, SI_UTF8_SIGNATURE
, 3) == 0) {
1206 // determine the length of the converted data
1207 size_t uLen
= converter
.SizeFromStore(a_pData
, a_uDataLen
);
1208 if (uLen
== (size_t)(-1)) {
1212 // allocate memory for the data, ensure that there is a NULL
1213 // terminator wherever the converted data ends
1214 SI_CHAR
* pData
= new SI_CHAR
[uLen
+1];
1218 memset(pData
, 0, sizeof(SI_CHAR
)*(uLen
+1));
1221 if (!converter
.ConvertFromStore(a_pData
, a_uDataLen
, pData
, uLen
)) {
1227 const static SI_CHAR empty
= 0;
1228 SI_CHAR
* pWork
= pData
;
1229 const SI_CHAR
* pSection
= &empty
;
1230 const SI_CHAR
* pItem
= NULL
;
1231 const SI_CHAR
* pVal
= NULL
;
1232 const SI_CHAR
* pComment
= NULL
;
1234 // We copy the strings if we are loading data into this class when we
1235 // already have stored some.
1236 bool bCopyStrings
= (m_pData
!= NULL
);
1238 // find a file comment if it exists, this is a comment that starts at the
1239 // beginning of the file and continues until the first blank line.
1240 SI_Error rc
= FindFileComment(pWork
, bCopyStrings
);
1241 if (rc
< 0) return rc
;
1243 // add every entry in the file to the data table
1244 while (FindEntry(pWork
, pSection
, pItem
, pVal
, pComment
)) {
1245 rc
= AddEntry(pSection
, pItem
, pVal
, pComment
, bCopyStrings
);
1246 if (rc
< 0) return rc
;
1249 // store these strings if we didn't copy them
1255 m_uDataLen
= uLen
+1;
1261 #ifdef SI_SUPPORT_IOSTREAMS
1262 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1264 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::Load(
1265 std::istream
& a_istream
1268 std::string strData
;
1271 a_istream
.get(szBuf
, sizeof(szBuf
), '\0');
1272 strData
.append(szBuf
);
1274 while (a_istream
.good());
1275 return Load(strData
);
1277 #endif // SI_SUPPORT_IOSTREAMS
1279 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1281 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::FindFileComment(
1286 // there can only be a single file comment
1287 if (m_pFileComment
) {
1291 // Load the file comment as multi-line text, this will modify all of
1292 // the newline characters to be single \n chars
1293 if (!LoadMultiLineText(a_pData
, m_pFileComment
, NULL
, false)) {
1297 // copy the string if necessary
1298 if (a_bCopyStrings
) {
1299 SI_Error rc
= CopyString(m_pFileComment
);
1300 if (rc
< 0) return rc
;
1306 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1308 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::FindEntry(
1310 const SI_CHAR
*& a_pSection
,
1311 const SI_CHAR
*& a_pKey
,
1312 const SI_CHAR
*& a_pVal
,
1313 const SI_CHAR
*& a_pComment
1318 SI_CHAR
* pTrail
= NULL
;
1320 // skip spaces and empty lines
1321 while (*a_pData
&& IsSpace(*a_pData
)) {
1328 // skip processing of comment lines but keep a pointer to
1329 // the start of the comment.
1330 if (IsComment(*a_pData
)) {
1331 LoadMultiLineText(a_pData
, a_pComment
, NULL
, true);
1335 // process section names
1336 if (*a_pData
== '[') {
1337 // skip leading spaces
1339 while (*a_pData
&& IsSpace(*a_pData
)) {
1343 // find the end of the section name (it may contain spaces)
1344 // and convert it to lowercase as necessary
1345 a_pSection
= a_pData
;
1346 while (*a_pData
&& *a_pData
!= ']' && !IsNewLineChar(*a_pData
)) {
1350 // if it's an invalid line, just skip it
1351 if (*a_pData
!= ']') {
1355 // remove trailing spaces from the section
1356 pTrail
= a_pData
- 1;
1357 while (pTrail
>= a_pSection
&& IsSpace(*pTrail
)) {
1363 // skip to the end of the line
1364 ++a_pData
; // safe as checked that it == ']' above
1365 while (*a_pData
&& !IsNewLineChar(*a_pData
)) {
1374 // find the end of the key name (it may contain spaces)
1375 // and convert it to lowercase as necessary
1377 while (*a_pData
&& *a_pData
!= '=' && !IsNewLineChar(*a_pData
)) {
1381 // if it's an invalid line, just skip it
1382 if (*a_pData
!= '=') {
1386 // empty keys are invalid
1387 if (a_pKey
== a_pData
) {
1388 while (*a_pData
&& !IsNewLineChar(*a_pData
)) {
1394 // remove trailing spaces from the key
1395 pTrail
= a_pData
- 1;
1396 while (pTrail
>= a_pKey
&& IsSpace(*pTrail
)) {
1402 // skip leading whitespace on the value
1403 ++a_pData
; // safe as checked that it == '=' above
1404 while (*a_pData
&& !IsNewLineChar(*a_pData
) && IsSpace(*a_pData
)) {
1408 // find the end of the value which is the end of this line
1410 while (*a_pData
&& !IsNewLineChar(*a_pData
)) {
1414 // remove trailing spaces from the value
1415 pTrail
= a_pData
- 1;
1416 if (*a_pData
) { // prepare for the next round
1417 SkipNewLine(a_pData
);
1419 while (pTrail
>= a_pVal
&& IsSpace(*pTrail
)) {
1425 // check for multi-line entries
1426 if (m_bAllowMultiLine
&& IsMultiLineTag(a_pVal
)) {
1427 // skip the "<<<" to get the tag that will end the multiline
1428 const SI_CHAR
* pTagName
= a_pVal
+ 3;
1429 return LoadMultiLineText(a_pData
, a_pVal
, pTagName
);
1432 // return the standard entry
1439 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1441 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::IsMultiLineTag(
1442 const SI_CHAR
* a_pVal
1445 // check for the "<<<" prefix for a multi-line entry
1446 if (*a_pVal
++ != '<') return false;
1447 if (*a_pVal
++ != '<') return false;
1448 if (*a_pVal
++ != '<') return false;
1452 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1454 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::IsMultiLineData(
1455 const SI_CHAR
* a_pData
1458 // data is multi-line if it has any of the following features:
1459 // * whitespace prefix
1460 // * embedded newlines
1461 // * whitespace suffix
1469 if (IsSpace(*a_pData
)) {
1473 // embedded newlines
1475 if (IsNewLineChar(*a_pData
)) {
1482 if (IsSpace(*--a_pData
)) {
1489 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1491 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::IsNewLineChar(
1495 return (a_c
== '\n' || a_c
== '\r');
1498 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1500 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::LoadMultiLineText(
1502 const SI_CHAR
*& a_pVal
,
1503 const SI_CHAR
* a_pTagName
,
1504 bool a_bAllowBlankLinesInComment
1507 // we modify this data to strip all newlines down to a single '\n'
1508 // character. This means that on Windows we need to strip out some
1509 // characters which will make the data shorter.
1510 // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
1511 // LINE1-LINE1\nLINE2-LINE2\0
1512 // The pDataLine entry is the pointer to the location in memory that
1513 // the current line needs to start to run following the existing one.
1514 // This may be the same as pCurrLine in which case no move is needed.
1515 SI_CHAR
* pDataLine
= a_pData
;
1516 SI_CHAR
* pCurrLine
;
1518 // value starts at the current line
1521 // find the end tag. This tag must start in column 1 and be
1522 // followed by a newline. No whitespace removal is done while
1523 // searching for this tag.
1524 SI_CHAR cEndOfLineChar
= *a_pData
;
1526 // if we are loading comments then we need a comment character as
1527 // the first character on every line
1528 if (!a_pTagName
&& !IsComment(*a_pData
)) {
1529 // if we aren't allowing blank lines then we're done
1530 if (!a_bAllowBlankLinesInComment
) {
1534 // if we are allowing blank lines then we only include them
1535 // in this comment if another comment follows, so read ahead
1537 SI_CHAR
* pCurr
= a_pData
;
1539 while (IsSpace(*pCurr
)) {
1540 if (IsNewLineChar(*pCurr
)) {
1549 // we have a comment, add the blank lines to the output
1550 // and continue processing from here
1551 if (IsComment(*pCurr
)) {
1552 for (; nNewLines
> 0; --nNewLines
) *pDataLine
++ = '\n';
1557 // the comment ends here
1561 // find the end of this line
1562 pCurrLine
= a_pData
;
1563 while (*a_pData
&& !IsNewLineChar(*a_pData
)) ++a_pData
;
1565 // move this line down to the location that it should be if necessary
1566 if (pDataLine
< pCurrLine
) {
1567 size_t nLen
= (size_t) (a_pData
- pCurrLine
);
1568 memmove(pDataLine
, pCurrLine
, nLen
* sizeof(SI_CHAR
));
1569 pDataLine
[nLen
] = '\0';
1572 // end the line with a NULL
1573 cEndOfLineChar
= *a_pData
;
1576 // if are looking for a tag then do the check now. This is done before
1577 // checking for end of the data, so that if we have the tag at the end
1578 // of the data then the tag is removed correctly.
1580 (!IsLess(pDataLine
, a_pTagName
) && !IsLess(a_pTagName
, pDataLine
)))
1585 // if we are at the end of the data then we just automatically end
1586 // this entry and return the current data.
1587 if (!cEndOfLineChar
) {
1591 // otherwise we need to process this newline to ensure that it consists
1592 // of just a single \n character.
1593 pDataLine
+= (a_pData
- pCurrLine
);
1594 *a_pData
= cEndOfLineChar
;
1595 SkipNewLine(a_pData
);
1596 *pDataLine
++ = '\n';
1599 // if we didn't find a comment at all then return false
1600 if (a_pVal
== a_pData
) {
1605 // the data (which ends at the end of the last line) needs to be
1606 // null-terminated BEFORE before the newline character(s). If the
1607 // user wants a new line in the multi-line data then they need to
1608 // add an empty line before the tag.
1609 *--pDataLine
= '\0';
1611 // if looking for a tag and if we aren't at the end of the data,
1612 // then move a_pData to the start of the next line.
1613 if (a_pTagName
&& cEndOfLineChar
) {
1614 SI_ASSERT(IsNewLineChar(cEndOfLineChar
));
1615 *a_pData
= cEndOfLineChar
;
1616 SkipNewLine(a_pData
);
1622 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1624 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::CopyString(
1625 const SI_CHAR
*& a_pString
1629 if (sizeof(SI_CHAR
) == sizeof(char)) {
1630 uLen
= strlen((const char *)a_pString
);
1632 else if (sizeof(SI_CHAR
) == sizeof(wchar_t)) {
1633 uLen
= wcslen((const wchar_t *)a_pString
);
1636 for ( ; a_pString
[uLen
]; ++uLen
) /*loop*/ ;
1638 ++uLen
; // NULL character
1639 SI_CHAR
* pCopy
= new SI_CHAR
[uLen
];
1643 memcpy(pCopy
, a_pString
, sizeof(SI_CHAR
)*uLen
);
1644 m_strings
.push_back(pCopy
);
1649 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1651 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::AddEntry(
1652 const SI_CHAR
* a_pSection
,
1653 const SI_CHAR
* a_pKey
,
1654 const SI_CHAR
* a_pValue
,
1655 const SI_CHAR
* a_pComment
,
1660 bool bInserted
= false;
1662 SI_ASSERT(!a_pComment
|| IsComment(*a_pComment
));
1664 // if we are copying strings then make a copy of the comment now
1665 // because we will need it when we add the entry.
1666 if (a_bCopyStrings
&& a_pComment
) {
1667 rc
= CopyString(a_pComment
);
1668 if (rc
< 0) return rc
;
1671 // check for existence of the section first if we need string copies
1672 typename
TSection::iterator iSection
= m_data
.end();
1673 if (a_bCopyStrings
) {
1674 iSection
= m_data
.find(a_pSection
);
1675 if (iSection
== m_data
.end()) {
1676 // if the section doesn't exist then we need a copy as the
1677 // string needs to last beyond the end of this function
1678 // because we will be inserting the section next
1679 rc
= CopyString(a_pSection
);
1680 if (rc
< 0) return rc
;
1684 // create the section entry
1685 if (iSection
== m_data
.end()) {
1686 Entry
oKey(a_pSection
, ++m_nOrder
);
1687 if (a_pComment
&& (!a_pKey
|| !a_pValue
)) {
1688 oKey
.pComment
= a_pComment
;
1690 typename
TSection::value_type
oEntry(oKey
, TKeyVal());
1691 typedef typename
TSection::iterator SectionIterator
;
1692 std::pair
<SectionIterator
,bool> i
=
1693 m_data
.insert(oEntry
);
1697 if (!a_pKey
|| !a_pValue
) {
1698 // section only entries are specified with pItem and pVal as NULL
1699 return bInserted
? SI_INSERTED
: SI_UPDATED
;
1702 // check for existence of the key
1703 TKeyVal
& keyval
= iSection
->second
;
1704 typename
TKeyVal::iterator iKey
= keyval
.find(a_pKey
);
1706 // make string copies if necessary
1707 if (a_bCopyStrings
) {
1708 if (m_bAllowMultiKey
|| iKey
== keyval
.end()) {
1709 // if the key doesn't exist then we need a copy as the
1710 // string needs to last beyond the end of this function
1711 // because we will be inserting the key next
1712 rc
= CopyString(a_pKey
);
1713 if (rc
< 0) return rc
;
1716 // we always need a copy of the value
1717 rc
= CopyString(a_pValue
);
1718 if (rc
< 0) return rc
;
1721 // create the key entry
1722 if (iKey
== keyval
.end() || m_bAllowMultiKey
) {
1723 Entry
oKey(a_pKey
, ++m_nOrder
);
1725 oKey
.pComment
= a_pComment
;
1727 typename
TKeyVal::value_type
oEntry(oKey
, NULL
);
1728 iKey
= keyval
.insert(oEntry
);
1731 iKey
->second
= a_pValue
;
1732 return bInserted
? SI_INSERTED
: SI_UPDATED
;
1735 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1737 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetValue(
1738 const SI_CHAR
* a_pSection
,
1739 const SI_CHAR
* a_pKey
,
1740 const SI_CHAR
* a_pDefault
,
1741 bool * a_pHasMultiple
1744 if (a_pHasMultiple
) {
1745 *a_pHasMultiple
= false;
1747 if (!a_pSection
|| !a_pKey
) {
1750 typename
TSection::const_iterator iSection
= m_data
.find(a_pSection
);
1751 if (iSection
== m_data
.end()) {
1754 typename
TKeyVal::const_iterator iKeyVal
= iSection
->second
.find(a_pKey
);
1755 if (iKeyVal
== iSection
->second
.end()) {
1759 // check for multiple entries with the same key
1760 if (m_bAllowMultiKey
&& a_pHasMultiple
) {
1761 typename
TKeyVal::const_iterator iTemp
= iKeyVal
;
1762 if (++iTemp
!= iSection
->second
.end()) {
1763 if (!IsLess(a_pKey
, iTemp
->first
.pItem
)) {
1764 *a_pHasMultiple
= true;
1769 return iKeyVal
->second
;
1772 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1774 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetAllValues(
1775 const SI_CHAR
* a_pSection
,
1776 const SI_CHAR
* a_pKey
,
1777 TNamesDepend
& a_values
1780 if (!a_pSection
|| !a_pKey
) {
1783 typename
TSection::const_iterator iSection
= m_data
.find(a_pSection
);
1784 if (iSection
== m_data
.end()) {
1787 typename
TKeyVal::const_iterator iKeyVal
= iSection
->second
.find(a_pKey
);
1788 if (iKeyVal
== iSection
->second
.end()) {
1792 // insert all values for this key
1793 a_values
.push_back(iKeyVal
->second
);
1794 if (m_bAllowMultiKey
) {
1796 while (iKeyVal
!= iSection
->second
.end() && !IsLess(a_pKey
, iKeyVal
->first
.pItem
)) {
1797 a_values
.push_back(Entry(iKeyVal
->second
, iKeyVal
->first
.nOrder
));
1805 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1807 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetSectionSize(
1808 const SI_CHAR
* a_pSection
1815 typename
TSection::const_iterator iSection
= m_data
.find(a_pSection
);
1816 if (iSection
== m_data
.end()) {
1819 const TKeyVal
& section
= iSection
->second
;
1821 // if multi-key isn't permitted then the section size is
1822 // the number of keys that we have.
1823 if (!m_bAllowMultiKey
|| section
.empty()) {
1824 return (int) section
.size();
1827 // otherwise we need to count them
1829 const SI_CHAR
* pLastKey
= NULL
;
1830 typename
TKeyVal::const_iterator iKeyVal
= section
.begin();
1831 for (int n
= 0; iKeyVal
!= section
.end(); ++iKeyVal
, ++n
) {
1832 if (!pLastKey
|| IsLess(pLastKey
, iKeyVal
->first
.pItem
)) {
1834 pLastKey
= iKeyVal
->first
.pItem
;
1840 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1841 const typename CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::TKeyVal
*
1842 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetSection(
1843 const SI_CHAR
* a_pSection
1847 typename
TSection::const_iterator i
= m_data
.find(a_pSection
);
1848 if (i
!= m_data
.end()) {
1849 return &(i
->second
);
1855 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1857 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetAllSections(
1858 TNamesDepend
& a_names
1861 typename
TSection::const_iterator i
= m_data
.begin();
1862 for (int n
= 0; i
!= m_data
.end(); ++i
, ++n
) {
1863 a_names
.push_back(i
->first
);
1867 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1869 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetAllKeys(
1870 const SI_CHAR
* a_pSection
,
1871 TNamesDepend
& a_names
1878 typename
TSection::const_iterator iSection
= m_data
.find(a_pSection
);
1879 if (iSection
== m_data
.end()) {
1883 const TKeyVal
& section
= iSection
->second
;
1884 const SI_CHAR
* pLastKey
= NULL
;
1885 typename
TKeyVal::const_iterator iKeyVal
= section
.begin();
1886 for (int n
= 0; iKeyVal
!= section
.end(); ++iKeyVal
, ++n
) {
1887 if (!pLastKey
|| IsLess(pLastKey
, iKeyVal
->first
.pItem
)) {
1888 a_names
.push_back(iKeyVal
->first
);
1889 pLastKey
= iKeyVal
->first
.pItem
;
1896 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1898 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::SaveFile(
1899 const char * a_pszFile
,
1900 bool a_bAddSignature
1904 #if __STDC_WANT_SECURE_LIB__
1905 fopen_s(&fp
, a_pszFile
, "wb");
1907 fp
= fopen(a_pszFile
, "wb");
1909 if (!fp
) return SI_FILE
;
1910 SI_Error rc
= SaveFile(fp
, a_bAddSignature
);
1915 #ifdef SI_HAS_WIDE_FILE
1916 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1918 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::SaveFile(
1919 const SI_WCHAR_T
* a_pwszFile
,
1920 bool a_bAddSignature
1924 FILE * fp
= _wfopen(a_pwszFile
, L
"wb");
1925 if (!fp
) return SI_FILE
;
1926 SI_Error rc
= SaveFile(fp
, a_bAddSignature
);
1929 #else // SI_CONVERT_ICU
1931 u_austrncpy(szFile
, a_pwszFile
, sizeof(szFile
));
1932 return SaveFile(szFile
, a_bAddSignature
);
1935 #endif // SI_HAS_WIDE_FILE
1937 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1939 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::SaveFile(
1941 bool a_bAddSignature
1944 FileWriter
writer(a_pFile
);
1945 return Save(writer
, a_bAddSignature
);
1948 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1950 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::Save(
1951 OutputWriter
& a_oOutput
,
1952 bool a_bAddSignature
1955 Converter
convert(m_bStoreIsUtf8
);
1957 // add the UTF-8 signature if it is desired
1958 if (m_bStoreIsUtf8
&& a_bAddSignature
) {
1959 a_oOutput
.Write(SI_UTF8_SIGNATURE
);
1962 // get all of the sections sorted in load order
1963 TNamesDepend oSections
;
1964 GetAllSections(oSections
);
1965 #if defined(_MSC_VER) && _MSC_VER <= 1200
1968 oSections
.sort(typename
Entry::LoadOrder());
1971 // write the file comment if we have one
1972 bool bNeedNewLine
= false;
1973 if (m_pFileComment
) {
1974 if (!OutputMultiLineText(a_oOutput
, convert
, m_pFileComment
)) {
1977 bNeedNewLine
= true;
1980 // iterate through our sections and output the data
1981 typename
TNamesDepend::const_iterator iSection
= oSections
.begin();
1982 for ( ; iSection
!= oSections
.end(); ++iSection
) {
1983 // write out the comment if there is one
1984 if (iSection
->pComment
) {
1985 if (!convert
.ConvertToStore(iSection
->pComment
)) {
1989 a_oOutput
.Write(SI_NEWLINE_A
);
1990 a_oOutput
.Write(SI_NEWLINE_A
);
1992 a_oOutput
.Write(convert
.Data());
1993 a_oOutput
.Write(SI_NEWLINE_A
);
1994 bNeedNewLine
= false;
1998 a_oOutput
.Write(SI_NEWLINE_A
);
1999 a_oOutput
.Write(SI_NEWLINE_A
);
2000 bNeedNewLine
= false;
2003 // write the section (unless there is no section name)
2004 if (*iSection
->pItem
) {
2005 if (!convert
.ConvertToStore(iSection
->pItem
)) {
2008 a_oOutput
.Write("[");
2009 a_oOutput
.Write(convert
.Data());
2010 a_oOutput
.Write("]");
2011 a_oOutput
.Write(SI_NEWLINE_A
);
2014 // get all of the keys sorted in load order
2016 GetAllKeys(iSection
->pItem
, oKeys
);
2017 #if defined(_MSC_VER) && _MSC_VER <= 1200
2020 oKeys
.sort(typename
Entry::LoadOrder());
2023 // write all keys and values
2024 typename
TNamesDepend::const_iterator iKey
= oKeys
.begin();
2025 for ( ; iKey
!= oKeys
.end(); ++iKey
) {
2026 // get all values for this key
2027 TNamesDepend oValues
;
2028 GetAllValues(iSection
->pItem
, iKey
->pItem
, oValues
);
2030 // write out the comment if there is one
2031 if (iKey
->pComment
) {
2032 a_oOutput
.Write(SI_NEWLINE_A
);
2033 if (!OutputMultiLineText(a_oOutput
, convert
, iKey
->pComment
)) {
2038 typename
TNamesDepend::const_iterator iValue
= oValues
.begin();
2039 for ( ; iValue
!= oValues
.end(); ++iValue
) {
2041 if (!convert
.ConvertToStore(iKey
->pItem
)) {
2044 a_oOutput
.Write(convert
.Data());
2047 if (!convert
.ConvertToStore(iValue
->pItem
)) {
2050 a_oOutput
.Write("=");
2051 if (m_bAllowMultiLine
&& IsMultiLineData(iValue
->pItem
)) {
2052 // multi-line data needs to be processed specially to ensure
2053 // that we use the correct newline format for the current system
2054 a_oOutput
.Write("<<<SI-END-OF-MULTILINE-TEXT" SI_NEWLINE_A
);
2055 if (!OutputMultiLineText(a_oOutput
, convert
, iValue
->pItem
)) {
2058 a_oOutput
.Write("SI-END-OF-MULTILINE-TEXT");
2061 a_oOutput
.Write(convert
.Data());
2063 a_oOutput
.Write(SI_NEWLINE_A
);
2067 bNeedNewLine
= true;
2073 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
2075 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::OutputMultiLineText(
2076 OutputWriter
& a_oOutput
,
2077 Converter
& a_oConverter
,
2078 const SI_CHAR
* a_pText
2081 const SI_CHAR
* pEndOfLine
;
2082 SI_CHAR cEndOfLineChar
= *a_pText
;
2083 while (cEndOfLineChar
) {
2084 // find the end of this line
2085 pEndOfLine
= a_pText
;
2086 for (; *pEndOfLine
&& *pEndOfLine
!= '\n'; ++pEndOfLine
) /*loop*/ ;
2087 cEndOfLineChar
= *pEndOfLine
;
2089 // temporarily null terminate, convert and output the line
2090 *const_cast<SI_CHAR
*>(pEndOfLine
) = 0;
2091 if (!a_oConverter
.ConvertToStore(a_pText
)) {
2094 *const_cast<SI_CHAR
*>(pEndOfLine
) = cEndOfLineChar
;
2095 a_pText
+= (pEndOfLine
- a_pText
) + 1;
2096 a_oOutput
.Write(a_oConverter
.Data());
2097 a_oOutput
.Write(SI_NEWLINE_A
);
2102 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
2104 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::Delete(
2105 const SI_CHAR
* a_pSection
,
2106 const SI_CHAR
* a_pKey
,
2114 typename
TSection::iterator iSection
= m_data
.find(a_pSection
);
2115 if (iSection
== m_data
.end()) {
2119 // remove a single key if we have a keyname
2121 typename
TKeyVal::iterator iKeyVal
= iSection
->second
.find(a_pKey
);
2122 if (iKeyVal
== iSection
->second
.end()) {
2126 // remove any copied strings and then the key
2127 typename
TKeyVal::iterator iDelete
;
2129 iDelete
= iKeyVal
++;
2131 DeleteString(iDelete
->first
.pItem
);
2132 DeleteString(iDelete
->second
);
2133 iSection
->second
.erase(iDelete
);
2135 while (iKeyVal
!= iSection
->second
.end()
2136 && !IsLess(a_pKey
, iKeyVal
->first
.pItem
));
2138 // done now if the section is not empty or we are not pruning away
2139 // the empty sections. Otherwise let it fall through into the section
2141 if (!a_bRemoveEmpty
|| !iSection
->second
.empty()) {
2146 // delete all copied strings from this section. The actual
2147 // entries will be removed when the section is removed.
2148 typename
TKeyVal::iterator iKeyVal
= iSection
->second
.begin();
2149 for ( ; iKeyVal
!= iSection
->second
.end(); ++iKeyVal
) {
2150 DeleteString(iKeyVal
->first
.pItem
);
2151 DeleteString(iKeyVal
->second
);
2155 // delete the section itself
2156 DeleteString(iSection
->first
.pItem
);
2157 m_data
.erase(iSection
);
2162 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
2164 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::DeleteString(
2165 const SI_CHAR
* a_pString
2168 // strings may exist either inside the data block, or they will be
2169 // individually allocated and stored in m_strings. We only physically
2170 // delete those stored in m_strings.
2171 if (a_pString
< m_pData
|| a_pString
>= m_pData
+ m_uDataLen
) {
2172 typename
TNamesDepend::iterator i
= m_strings
.begin();
2173 for (;i
!= m_strings
.end(); ++i
) {
2174 if (a_pString
== i
->pItem
) {
2175 delete[] const_cast<SI_CHAR
*>(i
->pItem
);
2183 // ---------------------------------------------------------------------------
2184 // CONVERSION FUNCTIONS
2185 // ---------------------------------------------------------------------------
2187 // Defines the conversion classes for different libraries. Before including
2188 // SimpleIni.h, set the converter that you wish you use by defining one of the
2189 // following symbols.
2191 // SI_CONVERT_GENERIC Use the Unicode reference conversion library in
2192 // the accompanying files ConvertUTF.h/c
2193 // SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
2194 // ICU headers on include path and icuuc.lib
2195 // SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
2197 #if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
2199 # define SI_CONVERT_WIN32
2201 # define SI_CONVERT_GENERIC
2206 * Generic case-sensitive less than comparison. This class returns numerically
2207 * ordered ASCII case-sensitive text for all possible sizes and types of
2210 template<class SI_CHAR
>
2211 struct SI_GenericCase
{
2212 bool operator()(const SI_CHAR
* pLeft
, const SI_CHAR
* pRight
) const {
2214 for ( ;*pLeft
&& *pRight
; ++pLeft
, ++pRight
) {
2215 cmp
= (long) *pLeft
- (long) *pRight
;
2220 return *pRight
!= 0;
2225 * Generic ASCII case-insensitive less than comparison. This class returns
2226 * numerically ordered ASCII case-insensitive text for all possible sizes
2227 * and types of SI_CHAR. It is not safe for MBCS text comparison where
2228 * ASCII A-Z characters are used in the encoding of multi-byte characters.
2230 template<class SI_CHAR
>
2231 struct SI_GenericNoCase
{
2232 inline SI_CHAR
locase(SI_CHAR ch
) const {
2233 return (ch
< 'A' || ch
> 'Z') ? ch
: (ch
- 'A' + 'a');
2235 bool operator()(const SI_CHAR
* pLeft
, const SI_CHAR
* pRight
) const {
2237 for ( ;*pLeft
&& *pRight
; ++pLeft
, ++pRight
) {
2238 cmp
= (long) locase(*pLeft
) - (long) locase(*pRight
);
2243 return *pRight
!= 0;
2248 * Null conversion class for MBCS/UTF-8 to char (or equivalent).
2250 template<class SI_CHAR
>
2252 bool m_bStoreIsUtf8
;
2256 SI_ConvertA(bool a_bStoreIsUtf8
) : m_bStoreIsUtf8(a_bStoreIsUtf8
) { }
2258 /* copy and assignment */
2259 SI_ConvertA(const SI_ConvertA
& rhs
) { operator=(rhs
); }
2260 SI_ConvertA
& operator=(const SI_ConvertA
& rhs
) {
2261 m_bStoreIsUtf8
= rhs
.m_bStoreIsUtf8
;
2265 /** Calculate the number of SI_CHAR required for converting the input
2266 * from the storage format. The storage format is always UTF-8 or MBCS.
2268 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2269 * @param a_uInputDataLen Length of storage format data in bytes. This
2270 * must be the actual length of the data, including
2271 * NULL byte if NULL terminated string is required.
2272 * @return Number of SI_CHAR required by the string when
2273 * converted. If there are embedded NULL bytes in the
2274 * input data, only the string up and not including
2275 * the NULL byte will be converted.
2276 * @return -1 cast to size_t on a conversion error.
2278 size_t SizeFromStore(
2279 const char * a_pInputData
,
2280 size_t a_uInputDataLen
)
2283 SI_ASSERT(a_uInputDataLen
!= (size_t) -1);
2285 // ASCII/MBCS/UTF-8 needs no conversion
2286 return a_uInputDataLen
;
2289 /** Convert the input string from the storage format to SI_CHAR.
2290 * The storage format is always UTF-8 or MBCS.
2292 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2293 * @param a_uInputDataLen Length of storage format data in bytes. This
2294 * must be the actual length of the data, including
2295 * NULL byte if NULL terminated string is required.
2296 * @param a_pOutputData Pointer to the output buffer to received the
2298 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2299 * @return true if all of the input data was successfully
2302 bool ConvertFromStore(
2303 const char * a_pInputData
,
2304 size_t a_uInputDataLen
,
2305 SI_CHAR
* a_pOutputData
,
2306 size_t a_uOutputDataSize
)
2308 // ASCII/MBCS/UTF-8 needs no conversion
2309 if (a_uInputDataLen
> a_uOutputDataSize
) {
2312 memcpy(a_pOutputData
, a_pInputData
, a_uInputDataLen
);
2316 /** Calculate the number of char required by the storage format of this
2317 * data. The storage format is always UTF-8 or MBCS.
2319 * @param a_pInputData NULL terminated string to calculate the number of
2320 * bytes required to be converted to storage format.
2321 * @return Number of bytes required by the string when
2322 * converted to storage format. This size always
2323 * includes space for the terminating NULL character.
2324 * @return -1 cast to size_t on a conversion error.
2327 const SI_CHAR
* a_pInputData
)
2329 // ASCII/MBCS/UTF-8 needs no conversion
2330 return strlen((const char *)a_pInputData
) + 1;
2333 /** Convert the input string to the storage format of this data.
2334 * The storage format is always UTF-8 or MBCS.
2336 * @param a_pInputData NULL terminated source string to convert. All of
2337 * the data will be converted including the
2338 * terminating NULL character.
2339 * @param a_pOutputData Pointer to the buffer to receive the converted
2341 * @param a_uOutputDataSize Size of the output buffer in char.
2342 * @return true if all of the input data, including the
2343 * terminating NULL character was successfully
2346 bool ConvertToStore(
2347 const SI_CHAR
* a_pInputData
,
2348 char * a_pOutputData
,
2349 size_t a_uOutputDataSize
)
2351 // calc input string length (SI_CHAR type and size independent)
2352 size_t uInputLen
= strlen((const char *)a_pInputData
) + 1;
2353 if (uInputLen
> a_uOutputDataSize
) {
2357 // ascii/UTF-8 needs no conversion
2358 memcpy(a_pOutputData
, a_pInputData
, uInputLen
);
2364 // ---------------------------------------------------------------------------
2365 // SI_CONVERT_GENERIC
2366 // ---------------------------------------------------------------------------
2367 #ifdef SI_CONVERT_GENERIC
2369 #define SI_Case SI_GenericCase
2370 #define SI_NoCase SI_GenericNoCase
2373 #include "ConvertUTF.h"
2376 * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference
2377 * library functions. This can be used on all platforms.
2379 template<class SI_CHAR
>
2381 bool m_bStoreIsUtf8
;
2385 SI_ConvertW(bool a_bStoreIsUtf8
) : m_bStoreIsUtf8(a_bStoreIsUtf8
) { }
2387 /* copy and assignment */
2388 SI_ConvertW(const SI_ConvertW
& rhs
) { operator=(rhs
); }
2389 SI_ConvertW
& operator=(const SI_ConvertW
& rhs
) {
2390 m_bStoreIsUtf8
= rhs
.m_bStoreIsUtf8
;
2394 /** Calculate the number of SI_CHAR required for converting the input
2395 * from the storage format. The storage format is always UTF-8 or MBCS.
2397 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2398 * @param a_uInputDataLen Length of storage format data in bytes. This
2399 * must be the actual length of the data, including
2400 * NULL byte if NULL terminated string is required.
2401 * @return Number of SI_CHAR required by the string when
2402 * converted. If there are embedded NULL bytes in the
2403 * input data, only the string up and not including
2404 * the NULL byte will be converted.
2405 * @return -1 cast to size_t on a conversion error.
2407 size_t SizeFromStore(
2408 const char * a_pInputData
,
2409 size_t a_uInputDataLen
)
2411 SI_ASSERT(a_uInputDataLen
!= (size_t) -1);
2413 if (m_bStoreIsUtf8
) {
2414 // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
2415 // so we just return the same number of characters required as for
2417 return a_uInputDataLen
;
2420 return mbstowcs(NULL
, a_pInputData
, a_uInputDataLen
);
2424 /** Convert the input string from the storage format to SI_CHAR.
2425 * The storage format is always UTF-8 or MBCS.
2427 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2428 * @param a_uInputDataLen Length of storage format data in bytes. This
2429 * must be the actual length of the data, including
2430 * NULL byte if NULL terminated string is required.
2431 * @param a_pOutputData Pointer to the output buffer to received the
2433 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2434 * @return true if all of the input data was successfully
2437 bool ConvertFromStore(
2438 const char * a_pInputData
,
2439 size_t a_uInputDataLen
,
2440 SI_CHAR
* a_pOutputData
,
2441 size_t a_uOutputDataSize
)
2443 if (m_bStoreIsUtf8
) {
2444 // This uses the Unicode reference implementation to do the
2445 // conversion from UTF-8 to wchar_t. The required files are
2446 // ConvertUTF.h and ConvertUTF.c which should be included in
2447 // the distribution but are publically available from unicode.org
2448 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
2449 ConversionResult retval
;
2450 const UTF8
* pUtf8
= (const UTF8
*) a_pInputData
;
2451 if (sizeof(wchar_t) == sizeof(UTF32
)) {
2452 UTF32
* pUtf32
= (UTF32
*) a_pOutputData
;
2453 retval
= ConvertUTF8toUTF32(
2454 &pUtf8
, pUtf8
+ a_uInputDataLen
,
2455 &pUtf32
, pUtf32
+ a_uOutputDataSize
,
2458 else if (sizeof(wchar_t) == sizeof(UTF16
)) {
2459 UTF16
* pUtf16
= (UTF16
*) a_pOutputData
;
2460 retval
= ConvertUTF8toUTF16(
2461 &pUtf8
, pUtf8
+ a_uInputDataLen
,
2462 &pUtf16
, pUtf16
+ a_uOutputDataSize
,
2465 return retval
== conversionOK
;
2468 size_t retval
= mbstowcs(a_pOutputData
,
2469 a_pInputData
, a_uOutputDataSize
);
2470 return retval
!= (size_t)(-1);
2474 /** Calculate the number of char required by the storage format of this
2475 * data. The storage format is always UTF-8 or MBCS.
2477 * @param a_pInputData NULL terminated string to calculate the number of
2478 * bytes required to be converted to storage format.
2479 * @return Number of bytes required by the string when
2480 * converted to storage format. This size always
2481 * includes space for the terminating NULL character.
2482 * @return -1 cast to size_t on a conversion error.
2485 const SI_CHAR
* a_pInputData
)
2487 if (m_bStoreIsUtf8
) {
2488 // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
2490 while (a_pInputData
[uLen
]) {
2493 return (6 * uLen
) + 1;
2496 size_t uLen
= wcstombs(NULL
, a_pInputData
, 0);
2497 if (uLen
== (size_t)(-1)) {
2500 return uLen
+ 1; // include NULL terminator
2504 /** Convert the input string to the storage format of this data.
2505 * The storage format is always UTF-8 or MBCS.
2507 * @param a_pInputData NULL terminated source string to convert. All of
2508 * the data will be converted including the
2509 * terminating NULL character.
2510 * @param a_pOutputData Pointer to the buffer to receive the converted
2512 * @param a_uOutputDataSize Size of the output buffer in char.
2513 * @return true if all of the input data, including the
2514 * terminating NULL character was successfully
2517 bool ConvertToStore(
2518 const SI_CHAR
* a_pInputData
,
2519 char * a_pOutputData
,
2520 size_t a_uOutputDataSize
2523 if (m_bStoreIsUtf8
) {
2524 // calc input string length (SI_CHAR type and size independent)
2525 size_t uInputLen
= 0;
2526 while (a_pInputData
[uInputLen
]) {
2529 ++uInputLen
; // include the NULL char
2531 // This uses the Unicode reference implementation to do the
2532 // conversion from wchar_t to UTF-8. The required files are
2533 // ConvertUTF.h and ConvertUTF.c which should be included in
2534 // the distribution but are publically available from unicode.org
2535 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
2536 ConversionResult retval
;
2537 UTF8
* pUtf8
= (UTF8
*) a_pOutputData
;
2538 if (sizeof(wchar_t) == sizeof(UTF32
)) {
2539 const UTF32
* pUtf32
= (const UTF32
*) a_pInputData
;
2540 retval
= ConvertUTF32toUTF8(
2541 &pUtf32
, pUtf32
+ uInputLen
+ 1,
2542 &pUtf8
, pUtf8
+ a_uOutputDataSize
,
2545 else if (sizeof(wchar_t) == sizeof(UTF16
)) {
2546 const UTF16
* pUtf16
= (const UTF16
*) a_pInputData
;
2547 retval
= ConvertUTF16toUTF8(
2548 &pUtf16
, pUtf16
+ uInputLen
+ 1,
2549 &pUtf8
, pUtf8
+ a_uOutputDataSize
,
2552 return retval
== conversionOK
;
2555 size_t retval
= wcstombs(a_pOutputData
,
2556 a_pInputData
, a_uOutputDataSize
);
2557 return retval
!= (size_t) -1;
2562 #endif // SI_CONVERT_GENERIC
2565 // ---------------------------------------------------------------------------
2567 // ---------------------------------------------------------------------------
2568 #ifdef SI_CONVERT_ICU
2570 #define SI_Case SI_GenericCase
2571 #define SI_NoCase SI_GenericNoCase
2573 #include <unicode/ucnv.h>
2576 * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms.
2578 template<class SI_CHAR
>
2580 const char * m_pEncoding
;
2581 UConverter
* m_pConverter
;
2583 SI_ConvertW() : m_pEncoding(NULL
), m_pConverter(NULL
) { }
2585 SI_ConvertW(bool a_bStoreIsUtf8
) : m_pConverter(NULL
) {
2586 m_pEncoding
= a_bStoreIsUtf8
? "UTF-8" : NULL
;
2589 /* copy and assignment */
2590 SI_ConvertW(const SI_ConvertW
& rhs
) { operator=(rhs
); }
2591 SI_ConvertW
& operator=(const SI_ConvertW
& rhs
) {
2592 m_pEncoding
= rhs
.m_pEncoding
;
2593 m_pConverter
= NULL
;
2596 ~SI_ConvertW() { if (m_pConverter
) ucnv_close(m_pConverter
); }
2598 /** Calculate the number of UChar required for converting the input
2599 * from the storage format. The storage format is always UTF-8 or MBCS.
2601 * @param a_pInputData Data in storage format to be converted to UChar.
2602 * @param a_uInputDataLen Length of storage format data in bytes. This
2603 * must be the actual length of the data, including
2604 * NULL byte if NULL terminated string is required.
2605 * @return Number of UChar required by the string when
2606 * converted. If there are embedded NULL bytes in the
2607 * input data, only the string up and not including
2608 * the NULL byte will be converted.
2609 * @return -1 cast to size_t on a conversion error.
2611 size_t SizeFromStore(
2612 const char * a_pInputData
,
2613 size_t a_uInputDataLen
)
2615 SI_ASSERT(a_uInputDataLen
!= (size_t) -1);
2619 if (!m_pConverter
) {
2620 nError
= U_ZERO_ERROR
;
2621 m_pConverter
= ucnv_open(m_pEncoding
, &nError
);
2622 if (U_FAILURE(nError
)) {
2627 nError
= U_ZERO_ERROR
;
2628 ucnv_resetToUnicode(m_pConverter
);
2629 int32_t nLen
= ucnv_toUChars(m_pConverter
, NULL
, 0,
2630 a_pInputData
, (int32_t) a_uInputDataLen
, &nError
);
2631 if (nError
!= U_BUFFER_OVERFLOW_ERROR
) {
2635 return (size_t) nLen
;
2638 /** Convert the input string from the storage format to UChar.
2639 * The storage format is always UTF-8 or MBCS.
2641 * @param a_pInputData Data in storage format to be converted to UChar.
2642 * @param a_uInputDataLen Length of storage format data in bytes. This
2643 * must be the actual length of the data, including
2644 * NULL byte if NULL terminated string is required.
2645 * @param a_pOutputData Pointer to the output buffer to received the
2647 * @param a_uOutputDataSize Size of the output buffer in UChar.
2648 * @return true if all of the input data was successfully
2651 bool ConvertFromStore(
2652 const char * a_pInputData
,
2653 size_t a_uInputDataLen
,
2654 UChar
* a_pOutputData
,
2655 size_t a_uOutputDataSize
)
2659 if (!m_pConverter
) {
2660 nError
= U_ZERO_ERROR
;
2661 m_pConverter
= ucnv_open(m_pEncoding
, &nError
);
2662 if (U_FAILURE(nError
)) {
2667 nError
= U_ZERO_ERROR
;
2668 ucnv_resetToUnicode(m_pConverter
);
2669 ucnv_toUChars(m_pConverter
,
2670 a_pOutputData
, (int32_t) a_uOutputDataSize
,
2671 a_pInputData
, (int32_t) a_uInputDataLen
, &nError
);
2672 if (U_FAILURE(nError
)) {
2679 /** Calculate the number of char required by the storage format of this
2680 * data. The storage format is always UTF-8 or MBCS.
2682 * @param a_pInputData NULL terminated string to calculate the number of
2683 * bytes required to be converted to storage format.
2684 * @return Number of bytes required by the string when
2685 * converted to storage format. This size always
2686 * includes space for the terminating NULL character.
2687 * @return -1 cast to size_t on a conversion error.
2690 const UChar
* a_pInputData
)
2694 if (!m_pConverter
) {
2695 nError
= U_ZERO_ERROR
;
2696 m_pConverter
= ucnv_open(m_pEncoding
, &nError
);
2697 if (U_FAILURE(nError
)) {
2702 nError
= U_ZERO_ERROR
;
2703 ucnv_resetFromUnicode(m_pConverter
);
2704 int32_t nLen
= ucnv_fromUChars(m_pConverter
, NULL
, 0,
2705 a_pInputData
, -1, &nError
);
2706 if (nError
!= U_BUFFER_OVERFLOW_ERROR
) {
2710 return (size_t) nLen
+ 1;
2713 /** Convert the input string to the storage format of this data.
2714 * The storage format is always UTF-8 or MBCS.
2716 * @param a_pInputData NULL terminated source string to convert. All of
2717 * the data will be converted including the
2718 * terminating NULL character.
2719 * @param a_pOutputData Pointer to the buffer to receive the converted
2721 * @param a_pOutputDataSize Size of the output buffer in char.
2722 * @return true if all of the input data, including the
2723 * terminating NULL character was successfully
2726 bool ConvertToStore(
2727 const UChar
* a_pInputData
,
2728 char * a_pOutputData
,
2729 size_t a_uOutputDataSize
)
2733 if (!m_pConverter
) {
2734 nError
= U_ZERO_ERROR
;
2735 m_pConverter
= ucnv_open(m_pEncoding
, &nError
);
2736 if (U_FAILURE(nError
)) {
2741 nError
= U_ZERO_ERROR
;
2742 ucnv_resetFromUnicode(m_pConverter
);
2743 ucnv_fromUChars(m_pConverter
,
2744 a_pOutputData
, (int32_t) a_uOutputDataSize
,
2745 a_pInputData
, -1, &nError
);
2746 if (U_FAILURE(nError
)) {
2754 #endif // SI_CONVERT_ICU
2757 // ---------------------------------------------------------------------------
2759 // ---------------------------------------------------------------------------
2760 #ifdef SI_CONVERT_WIN32
2762 #define SI_Case SI_GenericCase
2764 // Windows CE doesn't have errno or MBCS libraries
2771 #include <windows.h>
2773 # define SI_NoCase SI_GenericNoCase
2774 #else // !SI_NO_MBCS
2776 * Case-insensitive comparison class using Win32 MBCS functions. This class
2777 * returns a case-insensitive semi-collation order for MBCS text. It may not
2778 * be safe for UTF-8 text returned in char format as we don't know what
2779 * characters will be folded by the function! Therefore, if you are using
2780 * SI_CHAR == char and SetUnicode(true), then you need to use the generic
2781 * SI_NoCase class instead.
2783 #include <mbstring.h>
2784 template<class SI_CHAR
>
2786 bool operator()(const SI_CHAR
* pLeft
, const SI_CHAR
* pRight
) const {
2787 if (sizeof(SI_CHAR
) == sizeof(char)) {
2788 return _mbsicmp((const unsigned char *)pLeft
,
2789 (const unsigned char *)pRight
) < 0;
2791 if (sizeof(SI_CHAR
) == sizeof(wchar_t)) {
2792 return _wcsicmp((const wchar_t *)pLeft
,
2793 (const wchar_t *)pRight
) < 0;
2795 return SI_GenericNoCase
<SI_CHAR
>()(pLeft
, pRight
);
2798 #endif // SI_NO_MBCS
2801 * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses
2802 * only the Win32 functions and doesn't require the external Unicode UTF-8
2803 * conversion library. It will not work on Windows 95 without using Microsoft
2804 * Layer for Unicode in your application.
2806 template<class SI_CHAR
>
2812 SI_ConvertW(bool a_bStoreIsUtf8
) {
2813 m_uCodePage
= a_bStoreIsUtf8
? CP_UTF8
: CP_ACP
;
2816 /* copy and assignment */
2817 SI_ConvertW(const SI_ConvertW
& rhs
) { operator=(rhs
); }
2818 SI_ConvertW
& operator=(const SI_ConvertW
& rhs
) {
2819 m_uCodePage
= rhs
.m_uCodePage
;
2823 /** Calculate the number of SI_CHAR required for converting the input
2824 * from the storage format. The storage format is always UTF-8 or MBCS.
2826 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2827 * @param a_uInputDataLen Length of storage format data in bytes. This
2828 * must be the actual length of the data, including
2829 * NULL byte if NULL terminated string is required.
2830 * @return Number of SI_CHAR required by the string when
2831 * converted. If there are embedded NULL bytes in the
2832 * input data, only the string up and not including
2833 * the NULL byte will be converted.
2834 * @return -1 cast to size_t on a conversion error.
2836 size_t SizeFromStore(
2837 const char * a_pInputData
,
2838 size_t a_uInputDataLen
)
2840 SI_ASSERT(a_uInputDataLen
!= (size_t) -1);
2842 int retval
= MultiByteToWideChar(
2844 a_pInputData
, (int) a_uInputDataLen
,
2846 return (size_t)(retval
> 0 ? retval
: -1);
2849 /** Convert the input string from the storage format to SI_CHAR.
2850 * The storage format is always UTF-8 or MBCS.
2852 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2853 * @param a_uInputDataLen Length of storage format data in bytes. This
2854 * must be the actual length of the data, including
2855 * NULL byte if NULL terminated string is required.
2856 * @param a_pOutputData Pointer to the output buffer to received the
2858 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2859 * @return true if all of the input data was successfully
2862 bool ConvertFromStore(
2863 const char * a_pInputData
,
2864 size_t a_uInputDataLen
,
2865 SI_CHAR
* a_pOutputData
,
2866 size_t a_uOutputDataSize
)
2868 int nSize
= MultiByteToWideChar(
2870 a_pInputData
, (int) a_uInputDataLen
,
2871 (wchar_t *) a_pOutputData
, (int) a_uOutputDataSize
);
2875 /** Calculate the number of char required by the storage format of this
2876 * data. The storage format is always UTF-8.
2878 * @param a_pInputData NULL terminated string to calculate the number of
2879 * bytes required to be converted to storage format.
2880 * @return Number of bytes required by the string when
2881 * converted to storage format. This size always
2882 * includes space for the terminating NULL character.
2883 * @return -1 cast to size_t on a conversion error.
2886 const SI_CHAR
* a_pInputData
)
2888 int retval
= WideCharToMultiByte(
2890 (const wchar_t *) a_pInputData
, -1,
2892 return (size_t) (retval
> 0 ? retval
: -1);
2895 /** Convert the input string to the storage format of this data.
2896 * The storage format is always UTF-8 or MBCS.
2898 * @param a_pInputData NULL terminated source string to convert. All of
2899 * the data will be converted including the
2900 * terminating NULL character.
2901 * @param a_pOutputData Pointer to the buffer to receive the converted
2903 * @param a_pOutputDataSize Size of the output buffer in char.
2904 * @return true if all of the input data, including the
2905 * terminating NULL character was successfully
2908 bool ConvertToStore(
2909 const SI_CHAR
* a_pInputData
,
2910 char * a_pOutputData
,
2911 size_t a_uOutputDataSize
)
2913 int retval
= WideCharToMultiByte(
2915 (const wchar_t *) a_pInputData
, -1,
2916 a_pOutputData
, (int) a_uOutputDataSize
, 0, 0);
2921 #endif // SI_CONVERT_WIN32
2924 // ---------------------------------------------------------------------------
2926 // ---------------------------------------------------------------------------
2928 typedef CSimpleIniTempl
<char,
2929 SI_NoCase
<char>,SI_ConvertA
<char> > CSimpleIniA
;
2930 typedef CSimpleIniTempl
<char,
2931 SI_Case
<char>,SI_ConvertA
<char> > CSimpleIniCaseA
;
2933 #if defined(SI_CONVERT_ICU)
2934 typedef CSimpleIniTempl
<UChar
,
2935 SI_NoCase
<UChar
>,SI_ConvertW
<UChar
> > CSimpleIniW
;
2936 typedef CSimpleIniTempl
<UChar
,
2937 SI_Case
<UChar
>,SI_ConvertW
<UChar
> > CSimpleIniCaseW
;
2939 typedef CSimpleIniTempl
<wchar_t,
2940 SI_NoCase
<wchar_t>,SI_ConvertW
<wchar_t> > CSimpleIniW
;
2941 typedef CSimpleIniTempl
<wchar_t,
2942 SI_Case
<wchar_t>,SI_ConvertW
<wchar_t> > CSimpleIniCaseW
;
2946 # define CSimpleIni CSimpleIniW
2947 # define CSimpleIniCase CSimpleIniCaseW
2948 # define SI_NEWLINE SI_NEWLINE_W
2950 # define CSimpleIni CSimpleIniA
2951 # define CSimpleIniCase CSimpleIniCaseA
2952 # define SI_NEWLINE SI_NEWLINE_A
2956 # pragma warning (pop)
2959 #endif // INCLUDED_SimpleIni_h