added control output of variable total_interactions before reading interactions.ini
[cluster_expansion.git] / SimpleIni.h
blob5571d640d3ef91da2e4859a8802a78d24ce1e09d
1 /** @mainpage
3 <table>
4 <tr><th>Library <td>SimpleIni
5 <tr><th>File <td>SimpleIni.h
6 <tr><th>Author <td>Brodie Thiesfield [code at jellycan dot com]
7 <tr><th>Source <td>http://code.jellycan.com/simpleini/
8 <tr><th>Version <td>4.5
9 </table>
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)
45 - Linux/gcc (-Wall)
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
55 platforms.
56 -# Declare an instance the appropriate class. Note that the following
57 definitions are just shortcuts for commonly used types. Other types
58 (PRUnichar, unsigned short, unsigned char) are also possible.
59 <table>
60 <tr><th>Interface <th>Case-sensitive <th>Load UTF-8 <th>Load MBCS <th>Typedef
61 <tr><th>SI_CONVERT_GENERIC
62 <tr><td>char <td>No <td>Yes <td>Yes #1 <td>CSimpleIniA
63 <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
64 <tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW
65 <tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
66 <tr><th>SI_CONVERT_WIN32
67 <tr><td>char <td>No <td>No #2 <td>Yes <td>CSimpleIniA
68 <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
69 <tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW
70 <tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
71 <tr><th>SI_CONVERT_ICU
72 <tr><td>char <td>No <td>Yes <td>Yes <td>CSimpleIniA
73 <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
74 <tr><td>UChar <td>No <td>Yes <td>Yes <td>CSimpleIniW
75 <tr><td>UChar <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
76 </table>
77 #1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.<br>
78 #2 Only affects Windows. On Windows this uses MBCS functions and
79 so may fold case incorrectly leading to uncertain results.
80 -# Call Load() or LoadFile() to load and parse the INI configuration file
81 -# Access and modify the data of the file using the following functions
82 <table>
83 <tr><td>GetAllSections <td>Return all section names
84 <tr><td>GetAllKeys <td>Return all key names within a section
85 <tr><td>GetAllValues <td>Return all values within a section & key
86 <tr><td>GetSection <td>Return all key names and values in a section
87 <tr><td>GetSectionSize <td>Return the number of keys in a section
88 <tr><td>GetValue <td>Return a value for a section & key
89 <tr><td>SetValue <td>Add or update a value for a section & key
90 <tr><td>Delete <td>Remove a section, or a key from a section
91 </table>
92 -# Call Save() or SaveFile() to save the INI configuration data
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
100 opened.
102 @section multiline MULTI-LINE VALUES
104 Values that span multiple lines are created using the following format.
106 <pre>
107 key = <<<ENDTAG
108 .... multiline value ....
109 ENDTAG
110 </pre>
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.
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
129 line in the file.
130 - Every section may have a single "section comment". It will start
131 with the first comment line following the file comment, or the last
132 data entry. It ends at the beginning of the section.
133 - Every key may have a single "key comment". This comment will start
134 with the first comment line following the section start, or the file
135 comment if there is no section name.
136 - MultiKey entries may have only a single comment and will take the
137 comment associated with the first key found.
138 - Comments are set at the time that the file, section or key is first
139 created. The only way to modify a comment on a section or a key is to
140 delete that entry and recreate it with the new comment. There is no
141 way to change the file comment.
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.
151 @section notes NOTES
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)
197 # pragma once
198 #endif
200 // Disable these warnings in MSVC:
201 // 4127 "conditional expression is constant" as the conversion classes trigger
202 // it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
203 // be optimized away in a release build.
204 // 4503 'insert' : decorated name length exceeded, name was truncated
205 // 4702 "unreachable code" as the MS STL header causes it in release mode.
206 // Again, the code causing the warning will be cleaned up by the compiler.
207 // 4786 "identifier truncated to 256 characters" as this is thrown hundreds
208 // of times VC6 as soon as STL is used.
209 #ifdef _MSC_VER
210 # pragma warning (push)
211 # pragma warning (disable: 4127 4503 4702 4786)
212 #endif
214 #include <string>
215 #include <map>
216 #include <list>
217 #include <algorithm>
218 #include <stdio.h>
220 #ifdef SI_SUPPORT_IOSTREAMS
221 # include <iostream>
222 #endif // SI_SUPPORT_IOSTREAMS
224 #ifdef _DEBUG
225 # ifndef assert
226 # include <cassert>
227 # endif
228 # define SI_ASSERT(x) assert(x)
229 #else
230 # define SI_ASSERT(x)
231 #endif
233 enum SI_Error {
234 SI_OK = 0, //!< No error
235 SI_UPDATED = 1, //!< An existing value was updated
236 SI_INSERTED = 2, //!< A new value was inserted
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"
246 #ifdef _WIN32
247 # define SI_NEWLINE_A "\r\n"
248 # define SI_NEWLINE_W L"\r\n"
249 #else // !_WIN32
250 # define SI_NEWLINE_A "\n"
251 # define SI_NEWLINE_W L"\n"
252 #endif // _WIN32
254 #if defined(SI_CONVERT_ICU)
255 # include <unicode/ustring.h>
256 #endif
258 #if defined(_WIN32)
259 # define SI_HAS_WIDE_FILE
260 # define SI_WCHAR_T wchar_t
261 #elif defined(SI_CONVERT_ICU)
262 # define SI_HAS_WIDE_FILE
263 # define SI_WCHAR_T UChar
264 #endif
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:
277 <table>
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
283 </table>
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
293 public:
294 /** key entry */
295 struct Entry {
296 const SI_CHAR * pItem;
297 const SI_CHAR * pComment;
298 int nOrder;
300 Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0)
301 : pItem(a_pszItem)
302 , pComment(NULL)
303 , nOrder(a_nOrder)
305 Entry(const Entry & rhs) { operator=(rhs); }
306 Entry & operator=(const Entry & rhs) {
307 pItem = rhs.pItem;
308 pComment = rhs.pComment;
309 nOrder = rhs.nOrder;
310 return *this;
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); }
317 #endif
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.
352 class OutputWriter {
353 public:
354 OutputWriter() { }
355 virtual ~OutputWriter() { }
356 virtual void Write(const char * a_pBuf) = 0;
357 private:
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 {
364 FILE * m_file;
365 public:
366 FileWriter(FILE * a_file) : m_file(a_file) { }
367 void Write(const char * a_pBuf) {
368 fputs(a_pBuf, m_file);
370 private:
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;
378 public:
379 StringWriter(std::string & a_string) : m_string(a_string) { }
380 void Write(const char * a_pBuf) {
381 m_string.append(a_pBuf);
383 private:
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;
392 public:
393 StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { }
394 void Write(const char * a_pBuf) {
395 m_ostream << a_pBuf;
397 private:
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 {
407 public:
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;
414 return *this;
416 bool ConvertToStore(const SI_CHAR * a_pszString) {
417 size_t uLen = SizeToStore(a_pszString);
418 if (uLen == (size_t)(-1)) {
419 return false;
421 while (uLen > m_scratch.size()) {
422 m_scratch.resize(m_scratch.size() * 2);
424 return SI_CONVERTER::ConvertToStore(
425 a_pszString,
426 const_cast<char*>(m_scratch.data()),
427 m_scratch.size());
429 const char * Data() { return m_scratch.data(); }
430 private:
431 std::string m_scratch;
434 public:
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.
443 CSimpleIniTempl(
444 bool a_bIsUtf8 = false,
445 bool a_bMultiKey = false,
446 bool a_bMultiLine = false
449 /** Destructor */
450 ~CSimpleIniTempl();
452 /** Deallocate all memory stored by this object */
453 void Reset();
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:
484 <pre>
485 [section]
486 test=value1
487 test=value2
488 </pre>
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
493 at any time.
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
507 at any time.
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 /*-----------------------------------------------------------------------*/
519 /** @}
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
526 current platform.
528 @return SI_Error See error definitions
530 SI_Error LoadFile(
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
541 SI_Error LoadFile(
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
553 SI_Error LoadFile(
554 FILE * a_fpFile
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
564 SI_Error Load(
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
586 SI_Error Load(
587 const char * a_pData,
588 size_t a_uDataLen
591 /*-----------------------------------------------------------------------*/
592 /** @}
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
599 current platform.
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
607 SI_Error SaveFile(
608 const char * a_pszFile,
609 bool a_bAddSignature = true
610 ) const;
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
623 SI_Error SaveFile(
624 const SI_WCHAR_T * a_pwszFile,
625 bool a_bAddSignature = true
626 ) const;
627 #endif // _WIN32
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
632 binary output.
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
641 SI_Error SaveFile(
642 FILE * a_pFile,
643 bool a_bAddSignature = false
644 ) const;
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:
649 <table>
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
654 </table>
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
677 SI_Error Save(
678 OutputWriter & a_oOutput,
679 bool a_bAddSignature = false
680 ) const;
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
694 SI_Error Save(
695 std::ostream & a_ostream,
696 bool a_bAddSignature = false
697 ) const
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
715 SI_Error Save(
716 std::string & a_sBuffer,
717 bool a_bAddSignature = false
718 ) const
720 StringWriter writer(a_sBuffer);
721 return Save(writer, a_bAddSignature);
724 /*-----------------------------------------------------------------------*/
725 /** @}
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
735 are in use!
737 @param a_names Vector that will receive all of the section
738 names. See note above!
740 void GetAllSections(
741 TNamesDepend & a_names
742 ) const;
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
750 are in use!
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.
759 bool GetAllKeys(
760 const SI_CHAR * a_pSection,
761 TNamesDepend & a_names
762 ) const;
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.
778 bool GetAllValues(
779 const SI_CHAR * a_pSection,
780 const SI_CHAR * a_pKey,
781 TNamesDepend & a_values
782 ) const;
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
793 int GetSectionSize(
794 const SI_CHAR * a_pSection
795 ) const;
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
805 are in use!
807 @param a_pSection Name of the section to return
808 @return boolean Was a section matching the supplied
809 name found.
811 const TKeyVal * GetSection(
812 const SI_CHAR * a_pSection
813 ) const;
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
837 ) const;
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
846 empty section.
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
862 SI_Error SetValue(
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
877 in this situation.
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
885 removed?
887 @return true Key or section was deleted.
888 @return false Key or section was not found.
890 bool Delete(
891 const SI_CHAR * a_pSection,
892 const SI_CHAR * a_pKey,
893 bool a_bRemoveEmpty = false
896 /*-----------------------------------------------------------------------*/
897 /** @}
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 /*-----------------------------------------------------------------------*/
910 /** @} */
912 private:
913 /** Parse the data looking for a file comment and store it if found.
915 SI_Error FindFileComment(
916 SI_CHAR *& a_pData,
917 bool a_bCopyStrings
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.
924 bool FindEntry(
925 SI_CHAR *& a_pData,
926 const SI_CHAR *& a_pSection,
927 const SI_CHAR *& a_pKey,
928 const SI_CHAR *& a_pVal,
929 const SI_CHAR *& a_pComment
930 ) const;
932 /** Add the section/key/value to our data.
934 @param a_pSection Section name. Sections will be created if they
935 don't already exist.
936 @param a_pKey Key name. May be NULL to create an empty section.
937 Existing entries will be updated. New entries will
938 be created.
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.
948 SI_Error AddEntry(
949 const SI_CHAR * a_pSection,
950 const SI_CHAR * a_pKey,
951 const SI_CHAR * a_pValue,
952 const SI_CHAR * a_pComment,
953 bool a_bCopyStrings
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(
987 SI_CHAR *& a_pData,
988 const SI_CHAR *& a_pVal,
989 const SI_CHAR * a_pTagName,
990 bool a_bAllowBlankLinesInComment = false
991 ) const;
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
998 ) const;
1000 private:
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.
1006 SI_CHAR * m_pData;
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
1010 buffer.
1012 size_t m_uDataLen;
1014 /** File comment for this data, if one exists. */
1015 const SI_CHAR * m_pFileComment;
1017 /** Parsed INI data. Section -> (Key -> Value). */
1018 TSection m_data;
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()
1022 has been called.
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.
1038 int m_nOrder;
1041 // ---------------------------------------------------------------------------
1042 // IMPLEMENTATION
1043 // ---------------------------------------------------------------------------
1045 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1046 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl(
1047 bool a_bIsUtf8,
1048 bool a_bAllowMultiKey,
1049 bool a_bAllowMultiLine
1051 : m_pData(0)
1052 , m_uDataLen(0)
1053 , m_pFileComment(NULL)
1054 , m_bStoreIsUtf8(a_bIsUtf8)
1055 , m_bAllowMultiKey(a_bAllowMultiKey)
1056 , m_bAllowMultiLine(a_bAllowMultiLine)
1057 , m_nOrder(0)
1060 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1061 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::~CSimpleIniTempl()
1063 Reset();
1066 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1067 void
1068 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Reset()
1070 // remove all data
1071 delete[] m_pData;
1072 m_pData = NULL;
1073 m_uDataLen = 0;
1074 m_pFileComment = NULL;
1075 if (!m_data.empty()) {
1076 m_data.erase(m_data.begin(), m_data.end());
1079 // remove all strings
1080 if (!m_strings.empty()) {
1081 typename TNamesDepend::iterator i = m_strings.begin();
1082 for (; i != m_strings.end(); ++i) {
1083 delete[] const_cast<SI_CHAR*>(i->pItem);
1085 m_strings.erase(m_strings.begin(), m_strings.end());
1089 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1090 SI_Error
1091 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
1092 const char * a_pszFile
1095 FILE * fp = NULL;
1096 #if __STDC_WANT_SECURE_LIB__
1097 fopen_s(&fp, a_pszFile, "rb");
1098 #else
1099 fp = fopen(a_pszFile, "rb");
1100 #endif
1101 if (!fp) {
1102 return SI_FILE;
1104 SI_Error rc = LoadFile(fp);
1105 fclose(fp);
1106 return rc;
1109 #ifdef SI_HAS_WIDE_FILE
1110 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1111 SI_Error
1112 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
1113 const SI_WCHAR_T * a_pwszFile
1116 #ifdef _WIN32
1117 FILE * fp = NULL;
1118 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1119 _wfopen_s(&fp, a_pwszFile, L"rb");
1120 #else
1121 fp = _wfopen(a_pwszFile, L"rb");
1122 #endif
1123 if (!fp) return SI_FILE;
1124 SI_Error rc = LoadFile(fp);
1125 fclose(fp);
1126 return rc;
1127 #else // SI_CONVERT_ICU
1128 char szFile[256];
1129 u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
1130 return LoadFile(szFile);
1131 #endif
1133 #endif // SI_HAS_WIDE_FILE
1135 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1136 SI_Error
1137 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
1138 FILE * a_fpFile
1141 // load the raw file data
1142 int retval = fseek(a_fpFile, 0, SEEK_END);
1143 if (retval != 0) {
1144 return SI_FILE;
1146 long lSize = ftell(a_fpFile);
1147 if (lSize < 0) {
1148 return SI_FILE;
1150 if (lSize == 0) {
1151 return SI_OK;
1153 char * pData = new char[lSize];
1154 if (!pData) {
1155 return SI_NOMEM;
1157 fseek(a_fpFile, 0, SEEK_SET);
1158 size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
1159 if (uRead != (size_t) lSize) {
1160 delete[] pData;
1161 return SI_FILE;
1164 // convert the raw data to unicode
1165 SI_Error rc = Load(pData, uRead);
1166 delete[] pData;
1167 return rc;
1170 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1171 SI_Error
1172 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Load(
1173 const char * a_pData,
1174 size_t a_uDataLen
1177 SI_CONVERTER converter(m_bStoreIsUtf8);
1179 if (a_uDataLen == 0) {
1180 return SI_OK;
1183 // consume the UTF-8 BOM if it exists
1184 if (m_bStoreIsUtf8 && a_uDataLen >= 3) {
1185 if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
1186 a_pData += 3;
1187 a_uDataLen -= 3;
1191 // determine the length of the converted data
1192 size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
1193 if (uLen == (size_t)(-1)) {
1194 return SI_FAIL;
1197 // allocate memory for the data, ensure that there is a NULL
1198 // terminator wherever the converted data ends
1199 SI_CHAR * pData = new SI_CHAR[uLen+1];
1200 if (!pData) {
1201 return SI_NOMEM;
1203 memset(pData, 0, sizeof(SI_CHAR)*(uLen+1));
1205 // convert the data
1206 if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
1207 delete[] pData;
1208 return SI_FAIL;
1211 // parse it
1212 const static SI_CHAR empty = 0;
1213 SI_CHAR * pWork = pData;
1214 const SI_CHAR * pSection = &empty;
1215 const SI_CHAR * pItem = NULL;
1216 const SI_CHAR * pVal = NULL;
1217 const SI_CHAR * pComment = NULL;
1219 // We copy the strings if we are loading data into this class when we
1220 // already have stored some.
1221 bool bCopyStrings = (m_pData != NULL);
1223 // find a file comment if it exists, this is a comment that starts at the
1224 // beginning of the file and continues until the first blank line.
1225 SI_Error rc = FindFileComment(pWork, bCopyStrings);
1226 if (rc < 0) return rc;
1228 // add every entry in the file to the data table
1229 while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
1230 rc = AddEntry(pSection, pItem, pVal, pComment, bCopyStrings);
1231 if (rc < 0) return rc;
1234 // store these strings if we didn't copy them
1235 if (bCopyStrings) {
1236 delete[] pData;
1238 else {
1239 m_pData = pData;
1240 m_uDataLen = uLen+1;
1243 return SI_OK;
1246 #ifdef SI_SUPPORT_IOSTREAMS
1247 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1248 SI_Error
1249 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Load(
1250 std::istream & a_istream
1253 std::string strData;
1254 char szBuf[512];
1255 do {
1256 a_istream.get(szBuf, sizeof(szBuf), '\0');
1257 strData.append(szBuf);
1259 while (a_istream.good());
1260 return Load(strData);
1262 #endif // SI_SUPPORT_IOSTREAMS
1264 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1265 SI_Error
1266 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindFileComment(
1267 SI_CHAR *& a_pData,
1268 bool a_bCopyStrings
1271 // there can only be a single file comment
1272 if (m_pFileComment) {
1273 return SI_OK;
1276 // Load the file comment as multi-line text, this will modify all of
1277 // the newline characters to be single \n chars
1278 if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
1279 return SI_OK;
1282 // copy the string if necessary
1283 if (a_bCopyStrings) {
1284 SI_Error rc = CopyString(m_pFileComment);
1285 if (rc < 0) return rc;
1288 return SI_OK;
1291 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1292 bool
1293 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
1294 SI_CHAR *& a_pData,
1295 const SI_CHAR *& a_pSection,
1296 const SI_CHAR *& a_pKey,
1297 const SI_CHAR *& a_pVal,
1298 const SI_CHAR *& a_pComment
1299 ) const
1301 a_pComment = NULL;
1303 SI_CHAR * pTrail = NULL;
1304 while (*a_pData) {
1305 // skip spaces and empty lines
1306 while (*a_pData && IsSpace(*a_pData)) {
1307 ++a_pData;
1309 if (!*a_pData) {
1310 break;
1313 // skip processing of comment lines but keep a pointer to
1314 // the start of the comment.
1315 if (IsComment(*a_pData)) {
1316 LoadMultiLineText(a_pData, a_pComment, NULL, true);
1317 continue;
1320 // process section names
1321 if (*a_pData == '[') {
1322 // skip leading spaces
1323 ++a_pData;
1324 while (*a_pData && IsSpace(*a_pData)) {
1325 ++a_pData;
1328 // find the end of the section name (it may contain spaces)
1329 // and convert it to lowercase as necessary
1330 a_pSection = a_pData;
1331 while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
1332 ++a_pData;
1335 // if it's an invalid line, just skip it
1336 if (*a_pData != ']') {
1337 continue;
1340 // remove trailing spaces from the section
1341 pTrail = a_pData - 1;
1342 while (pTrail >= a_pSection && IsSpace(*pTrail)) {
1343 --pTrail;
1345 ++pTrail;
1346 *pTrail = 0;
1348 // skip to the end of the line
1349 ++a_pData; // safe as checked that it == ']' above
1350 while (*a_pData && !IsNewLineChar(*a_pData)) {
1351 ++a_pData;
1354 a_pKey = NULL;
1355 a_pVal = NULL;
1356 return true;
1359 // find the end of the key name (it may contain spaces)
1360 // and convert it to lowercase as necessary
1361 a_pKey = a_pData;
1362 while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
1363 ++a_pData;
1366 // if it's an invalid line, just skip it
1367 if (*a_pData != '=') {
1368 continue;
1371 // empty keys are invalid
1372 if (a_pKey == a_pData) {
1373 while (*a_pData && !IsNewLineChar(*a_pData)) {
1374 ++a_pData;
1376 continue;
1379 // remove trailing spaces from the key
1380 pTrail = a_pData - 1;
1381 while (pTrail >= a_pKey && IsSpace(*pTrail)) {
1382 --pTrail;
1384 ++pTrail;
1385 *pTrail = 0;
1387 // skip leading whitespace on the value
1388 ++a_pData; // safe as checked that it == '=' above
1389 while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
1390 ++a_pData;
1393 // find the end of the value which is the end of this line
1394 a_pVal = a_pData;
1395 while (*a_pData && !IsNewLineChar(*a_pData)) {
1396 ++a_pData;
1399 // remove trailing spaces from the value
1400 pTrail = a_pData - 1;
1401 if (*a_pData) { // prepare for the next round
1402 SkipNewLine(a_pData);
1404 while (pTrail >= a_pVal && IsSpace(*pTrail)) {
1405 --pTrail;
1407 ++pTrail;
1408 *pTrail = 0;
1410 // check for multi-line entries
1411 if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
1412 // skip the "<<<" to get the tag that will end the multiline
1413 const SI_CHAR * pTagName = a_pVal + 3;
1414 return LoadMultiLineText(a_pData, a_pVal, pTagName);
1417 // return the standard entry
1418 return true;
1421 return false;
1424 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1425 bool
1426 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineTag(
1427 const SI_CHAR * a_pVal
1428 ) const
1430 // check for the "<<<" prefix for a multi-line entry
1431 if (*a_pVal++ != '<') return false;
1432 if (*a_pVal++ != '<') return false;
1433 if (*a_pVal++ != '<') return false;
1434 return true;
1437 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1438 bool
1439 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData(
1440 const SI_CHAR * a_pData
1441 ) const
1443 // data is multi-line if it has any of the following features:
1444 // * whitespace prefix
1445 // * embedded newlines
1446 // * whitespace suffix
1448 // empty string
1449 if (!*a_pData) {
1450 return false;
1453 // check for prefix
1454 if (IsSpace(*a_pData)) {
1455 return true;
1458 // embedded newlines
1459 while (*a_pData) {
1460 if (IsNewLineChar(*a_pData)) {
1461 return true;
1463 ++a_pData;
1466 // check for suffix
1467 if (IsSpace(*--a_pData)) {
1468 return true;
1471 return false;
1474 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1475 bool
1476 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar(
1477 SI_CHAR a_c
1478 ) const
1480 return (a_c == '\n' || a_c == '\r');
1483 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1484 bool
1485 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
1486 SI_CHAR *& a_pData,
1487 const SI_CHAR *& a_pVal,
1488 const SI_CHAR * a_pTagName,
1489 bool a_bAllowBlankLinesInComment
1490 ) const
1492 // we modify this data to strip all newlines down to a single '\n'
1493 // character. This means that on Windows we need to strip out some
1494 // characters which will make the data shorter.
1495 // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
1496 // LINE1-LINE1\nLINE2-LINE2\0
1497 // The pDataLine entry is the pointer to the location in memory that
1498 // the current line needs to start to run following the existing one.
1499 // This may be the same as pCurrLine in which case no move is needed.
1500 SI_CHAR * pDataLine = a_pData;
1501 SI_CHAR * pCurrLine;
1503 // value starts at the current line
1504 a_pVal = a_pData;
1506 // find the end tag. This tag must start in column 1 and be
1507 // followed by a newline. No whitespace removal is done while
1508 // searching for this tag.
1509 SI_CHAR cEndOfLineChar = *a_pData;
1510 for(;;) {
1511 // if we are loading comments then we need a comment character as
1512 // the first character on every line
1513 if (!a_pTagName && !IsComment(*a_pData)) {
1514 // if we aren't allowing blank lines then we're done
1515 if (!a_bAllowBlankLinesInComment) {
1516 break;
1519 // if we are allowing blank lines then we only include them
1520 // in this comment if another comment follows, so read ahead
1521 // to find out.
1522 SI_CHAR * pCurr = a_pData;
1523 int nNewLines = 0;
1524 while (IsSpace(*pCurr)) {
1525 if (IsNewLineChar(*pCurr)) {
1526 ++nNewLines;
1527 SkipNewLine(pCurr);
1529 else {
1530 ++pCurr;
1534 // we have a comment, add the blank lines to the output
1535 // and continue processing from here
1536 if (IsComment(*pCurr)) {
1537 for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n';
1538 a_pData = pCurr;
1539 continue;
1542 // the comment ends here
1543 break;
1546 // find the end of this line
1547 pCurrLine = a_pData;
1548 while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
1550 // move this line down to the location that it should be if necessary
1551 if (pDataLine < pCurrLine) {
1552 size_t nLen = (size_t) (a_pData - pCurrLine);
1553 memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
1554 pDataLine[nLen] = '\0';
1557 // end the line with a NULL
1558 cEndOfLineChar = *a_pData;
1559 *a_pData = 0;
1561 // if are looking for a tag then do the check now. This is done before
1562 // checking for end of the data, so that if we have the tag at the end
1563 // of the data then the tag is removed correctly.
1564 if (a_pTagName &&
1565 (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)))
1567 break;
1570 // if we are at the end of the data then we just automatically end
1571 // this entry and return the current data.
1572 if (!cEndOfLineChar) {
1573 return true;
1576 // otherwise we need to process this newline to ensure that it consists
1577 // of just a single \n character.
1578 pDataLine += (a_pData - pCurrLine);
1579 *a_pData = cEndOfLineChar;
1580 SkipNewLine(a_pData);
1581 *pDataLine++ = '\n';
1584 // if we didn't find a comment at all then return false
1585 if (a_pVal == a_pData) {
1586 a_pVal = NULL;
1587 return false;
1590 // the data (which ends at the end of the last line) needs to be
1591 // null-terminated BEFORE before the newline character(s). If the
1592 // user wants a new line in the multi-line data then they need to
1593 // add an empty line before the tag.
1594 *--pDataLine = '\0';
1596 // if looking for a tag and if we aren't at the end of the data,
1597 // then move a_pData to the start of the next line.
1598 if (a_pTagName && cEndOfLineChar) {
1599 SI_ASSERT(IsNewLineChar(cEndOfLineChar));
1600 *a_pData = cEndOfLineChar;
1601 SkipNewLine(a_pData);
1604 return true;
1607 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1608 SI_Error
1609 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString(
1610 const SI_CHAR *& a_pString
1613 size_t uLen = 0;
1614 if (sizeof(SI_CHAR) == sizeof(char)) {
1615 uLen = strlen((const char *)a_pString);
1617 else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
1618 uLen = wcslen((const wchar_t *)a_pString);
1620 else {
1621 for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
1623 ++uLen; // NULL character
1624 SI_CHAR * pCopy = new SI_CHAR[uLen];
1625 if (!pCopy) {
1626 return SI_NOMEM;
1628 memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
1629 m_strings.push_back(pCopy);
1630 a_pString = pCopy;
1631 return SI_OK;
1634 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1635 SI_Error
1636 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
1637 const SI_CHAR * a_pSection,
1638 const SI_CHAR * a_pKey,
1639 const SI_CHAR * a_pValue,
1640 const SI_CHAR * a_pComment,
1641 bool a_bCopyStrings
1644 SI_Error rc;
1645 bool bInserted = false;
1647 SI_ASSERT(!a_pComment || IsComment(*a_pComment));
1649 // if we are copying strings then make a copy of the comment now
1650 // because we will need it when we add the entry.
1651 if (a_bCopyStrings && a_pComment) {
1652 rc = CopyString(a_pComment);
1653 if (rc < 0) return rc;
1656 // check for existence of the section first if we need string copies
1657 typename TSection::iterator iSection = m_data.end();
1658 if (a_bCopyStrings) {
1659 iSection = m_data.find(a_pSection);
1660 if (iSection == m_data.end()) {
1661 // if the section doesn't exist then we need a copy as the
1662 // string needs to last beyond the end of this function
1663 // because we will be inserting the section next
1664 rc = CopyString(a_pSection);
1665 if (rc < 0) return rc;
1669 // create the section entry
1670 if (iSection == m_data.end()) {
1671 Entry oKey(a_pSection, ++m_nOrder);
1672 if (a_pComment && (!a_pKey || !a_pValue)) {
1673 oKey.pComment = a_pComment;
1675 typename TSection::value_type oEntry(oKey, TKeyVal());
1676 typedef typename TSection::iterator SectionIterator;
1677 std::pair<SectionIterator,bool> i =
1678 m_data.insert(oEntry);
1679 iSection = i.first;
1680 bInserted = true;
1682 if (!a_pKey || !a_pValue) {
1683 // section only entries are specified with pItem and pVal as NULL
1684 return bInserted ? SI_INSERTED : SI_UPDATED;
1687 // check for existence of the key
1688 TKeyVal & keyval = iSection->second;
1689 typename TKeyVal::iterator iKey = keyval.find(a_pKey);
1691 // make string copies if necessary
1692 if (a_bCopyStrings) {
1693 if (m_bAllowMultiKey || iKey == keyval.end()) {
1694 // if the key doesn't exist then we need a copy as the
1695 // string needs to last beyond the end of this function
1696 // because we will be inserting the key next
1697 rc = CopyString(a_pKey);
1698 if (rc < 0) return rc;
1701 // we always need a copy of the value
1702 rc = CopyString(a_pValue);
1703 if (rc < 0) return rc;
1706 // create the key entry
1707 if (iKey == keyval.end() || m_bAllowMultiKey) {
1708 Entry oKey(a_pKey, ++m_nOrder);
1709 if (a_pComment) {
1710 oKey.pComment = a_pComment;
1712 typename TKeyVal::value_type oEntry(oKey, NULL);
1713 iKey = keyval.insert(oEntry);
1714 bInserted = true;
1716 iKey->second = a_pValue;
1717 return bInserted ? SI_INSERTED : SI_UPDATED;
1720 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1721 const SI_CHAR *
1722 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetValue(
1723 const SI_CHAR * a_pSection,
1724 const SI_CHAR * a_pKey,
1725 const SI_CHAR * a_pDefault,
1726 bool * a_pHasMultiple
1727 ) const
1729 if (a_pHasMultiple) {
1730 *a_pHasMultiple = false;
1732 if (!a_pSection || !a_pKey) {
1733 return a_pDefault;
1735 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1736 if (iSection == m_data.end()) {
1737 return a_pDefault;
1739 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
1740 if (iKeyVal == iSection->second.end()) {
1741 return a_pDefault;
1744 // check for multiple entries with the same key
1745 if (m_bAllowMultiKey && a_pHasMultiple) {
1746 typename TKeyVal::const_iterator iTemp = iKeyVal;
1747 if (++iTemp != iSection->second.end()) {
1748 if (!IsLess(a_pKey, iTemp->first.pItem)) {
1749 *a_pHasMultiple = true;
1754 return iKeyVal->second;
1757 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1758 bool
1759 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues(
1760 const SI_CHAR * a_pSection,
1761 const SI_CHAR * a_pKey,
1762 TNamesDepend & a_values
1763 ) const
1765 if (!a_pSection || !a_pKey) {
1766 return false;
1768 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1769 if (iSection == m_data.end()) {
1770 return false;
1772 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
1773 if (iKeyVal == iSection->second.end()) {
1774 return false;
1777 // insert all values for this key
1778 a_values.push_back(iKeyVal->second);
1779 if (m_bAllowMultiKey) {
1780 ++iKeyVal;
1781 while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {
1782 a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.nOrder));
1783 ++iKeyVal;
1787 return true;
1790 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1792 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSectionSize(
1793 const SI_CHAR * a_pSection
1794 ) const
1796 if (!a_pSection) {
1797 return -1;
1800 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1801 if (iSection == m_data.end()) {
1802 return -1;
1804 const TKeyVal & section = iSection->second;
1806 // if multi-key isn't permitted then the section size is
1807 // the number of keys that we have.
1808 if (!m_bAllowMultiKey || section.empty()) {
1809 return (int) section.size();
1812 // otherwise we need to count them
1813 int nCount = 0;
1814 const SI_CHAR * pLastKey = NULL;
1815 typename TKeyVal::const_iterator iKeyVal = section.begin();
1816 for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
1817 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
1818 ++nCount;
1819 pLastKey = iKeyVal->first.pItem;
1822 return nCount;
1825 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1826 const typename CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::TKeyVal *
1827 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSection(
1828 const SI_CHAR * a_pSection
1829 ) const
1831 if (a_pSection) {
1832 typename TSection::const_iterator i = m_data.find(a_pSection);
1833 if (i != m_data.end()) {
1834 return &(i->second);
1837 return 0;
1840 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1841 void
1842 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections(
1843 TNamesDepend & a_names
1844 ) const
1846 typename TSection::const_iterator i = m_data.begin();
1847 for (int n = 0; i != m_data.end(); ++i, ++n ) {
1848 a_names.push_back(i->first);
1852 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1853 bool
1854 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllKeys(
1855 const SI_CHAR * a_pSection,
1856 TNamesDepend & a_names
1857 ) const
1859 if (!a_pSection) {
1860 return false;
1863 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1864 if (iSection == m_data.end()) {
1865 return false;
1868 const TKeyVal & section = iSection->second;
1869 const SI_CHAR * pLastKey = NULL;
1870 typename TKeyVal::const_iterator iKeyVal = section.begin();
1871 for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) {
1872 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
1873 a_names.push_back(iKeyVal->first);
1874 pLastKey = iKeyVal->first.pItem;
1878 return true;
1881 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1882 SI_Error
1883 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
1884 const char * a_pszFile,
1885 bool a_bAddSignature
1886 ) const
1888 FILE * fp = NULL;
1889 #if __STDC_WANT_SECURE_LIB__
1890 fopen_s(&fp, a_pszFile, "wb");
1891 #else
1892 fp = fopen(a_pszFile, "wb");
1893 #endif
1894 if (!fp) return SI_FILE;
1895 SI_Error rc = SaveFile(fp, a_bAddSignature);
1896 fclose(fp);
1897 return rc;
1900 #ifdef SI_HAS_WIDE_FILE
1901 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1902 SI_Error
1903 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
1904 const SI_WCHAR_T * a_pwszFile,
1905 bool a_bAddSignature
1906 ) const
1908 #ifdef _WIN32
1909 FILE * fp = _wfopen(a_pwszFile, L"wb");
1910 if (!fp) return SI_FILE;
1911 SI_Error rc = SaveFile(fp, a_bAddSignature);
1912 fclose(fp);
1913 return rc;
1914 #else // SI_CONVERT_ICU
1915 char szFile[256];
1916 u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
1917 return SaveFile(szFile, a_bAddSignature);
1918 #endif
1920 #endif // SI_HAS_WIDE_FILE
1922 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1923 SI_Error
1924 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
1925 FILE * a_pFile,
1926 bool a_bAddSignature
1927 ) const
1929 FileWriter writer(a_pFile);
1930 return Save(writer, a_bAddSignature);
1933 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1934 SI_Error
1935 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
1936 OutputWriter & a_oOutput,
1937 bool a_bAddSignature
1938 ) const
1940 Converter convert(m_bStoreIsUtf8);
1942 // add the UTF-8 signature if it is desired
1943 if (m_bStoreIsUtf8 && a_bAddSignature) {
1944 a_oOutput.Write(SI_UTF8_SIGNATURE);
1947 // get all of the sections sorted in load order
1948 TNamesDepend oSections;
1949 GetAllSections(oSections);
1950 #if defined(_MSC_VER) && _MSC_VER <= 1200
1951 oSections.sort();
1952 #else
1953 oSections.sort(typename Entry::LoadOrder());
1954 #endif
1956 // write the file comment if we have one
1957 bool bNeedNewLine = false;
1958 if (m_pFileComment) {
1959 if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
1960 return SI_FAIL;
1962 bNeedNewLine = true;
1965 // iterate through our sections and output the data
1966 typename TNamesDepend::const_iterator iSection = oSections.begin();
1967 for ( ; iSection != oSections.end(); ++iSection ) {
1968 // write out the comment if there is one
1969 if (iSection->pComment) {
1970 if (!convert.ConvertToStore(iSection->pComment)) {
1971 return SI_FAIL;
1973 if (bNeedNewLine) {
1974 a_oOutput.Write(SI_NEWLINE_A);
1975 a_oOutput.Write(SI_NEWLINE_A);
1977 a_oOutput.Write(convert.Data());
1978 a_oOutput.Write(SI_NEWLINE_A);
1979 bNeedNewLine = false;
1982 if (bNeedNewLine) {
1983 a_oOutput.Write(SI_NEWLINE_A);
1984 a_oOutput.Write(SI_NEWLINE_A);
1985 bNeedNewLine = false;
1988 // write the section (unless there is no section name)
1989 if (*iSection->pItem) {
1990 if (!convert.ConvertToStore(iSection->pItem)) {
1991 return SI_FAIL;
1993 a_oOutput.Write("[");
1994 a_oOutput.Write(convert.Data());
1995 a_oOutput.Write("]");
1996 a_oOutput.Write(SI_NEWLINE_A);
1999 // get all of the keys sorted in load order
2000 TNamesDepend oKeys;
2001 GetAllKeys(iSection->pItem, oKeys);
2002 #if defined(_MSC_VER) && _MSC_VER <= 1200
2003 oKeys.sort();
2004 #else
2005 oKeys.sort(typename Entry::LoadOrder());
2006 #endif
2008 // write all keys and values
2009 typename TNamesDepend::const_iterator iKey = oKeys.begin();
2010 for ( ; iKey != oKeys.end(); ++iKey) {
2011 // get all values for this key
2012 TNamesDepend oValues;
2013 GetAllValues(iSection->pItem, iKey->pItem, oValues);
2015 // write out the comment if there is one
2016 if (iKey->pComment) {
2017 a_oOutput.Write(SI_NEWLINE_A);
2018 if (!OutputMultiLineText(a_oOutput, convert, iKey->pComment)) {
2019 return SI_FAIL;
2023 typename TNamesDepend::const_iterator iValue = oValues.begin();
2024 for ( ; iValue != oValues.end(); ++iValue) {
2025 // write the key
2026 if (!convert.ConvertToStore(iKey->pItem)) {
2027 return SI_FAIL;
2029 a_oOutput.Write(convert.Data());
2031 // write the value
2032 if (!convert.ConvertToStore(iValue->pItem)) {
2033 return SI_FAIL;
2035 a_oOutput.Write("=");
2036 if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
2037 // multi-line data needs to be processed specially to ensure
2038 // that we use the correct newline format for the current system
2039 a_oOutput.Write("<<<SI-END-OF-MULTILINE-TEXT" SI_NEWLINE_A);
2040 if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
2041 return SI_FAIL;
2043 a_oOutput.Write("SI-END-OF-MULTILINE-TEXT");
2045 else {
2046 a_oOutput.Write(convert.Data());
2048 a_oOutput.Write(SI_NEWLINE_A);
2052 bNeedNewLine = true;
2055 return SI_OK;
2058 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2059 bool
2060 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::OutputMultiLineText(
2061 OutputWriter & a_oOutput,
2062 Converter & a_oConverter,
2063 const SI_CHAR * a_pText
2064 ) const
2066 const SI_CHAR * pEndOfLine;
2067 SI_CHAR cEndOfLineChar = *a_pText;
2068 while (cEndOfLineChar) {
2069 // find the end of this line
2070 pEndOfLine = a_pText;
2071 for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;
2072 cEndOfLineChar = *pEndOfLine;
2074 // temporarily null terminate, convert and output the line
2075 *const_cast<SI_CHAR*>(pEndOfLine) = 0;
2076 if (!a_oConverter.ConvertToStore(a_pText)) {
2077 return false;
2079 *const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar;
2080 a_pText += (pEndOfLine - a_pText) + 1;
2081 a_oOutput.Write(a_oConverter.Data());
2082 a_oOutput.Write(SI_NEWLINE_A);
2084 return true;
2087 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2088 bool
2089 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(
2090 const SI_CHAR * a_pSection,
2091 const SI_CHAR * a_pKey,
2092 bool a_bRemoveEmpty
2095 if (!a_pSection) {
2096 return false;
2099 typename TSection::iterator iSection = m_data.find(a_pSection);
2100 if (iSection == m_data.end()) {
2101 return false;
2104 // remove a single key if we have a keyname
2105 if (a_pKey) {
2106 typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
2107 if (iKeyVal == iSection->second.end()) {
2108 return false;
2111 // remove any copied strings and then the key
2112 typename TKeyVal::iterator iDelete;
2113 do {
2114 iDelete = iKeyVal++;
2116 DeleteString(iDelete->first.pItem);
2117 DeleteString(iDelete->second);
2118 iSection->second.erase(iDelete);
2120 while (iKeyVal != iSection->second.end()
2121 && !IsLess(a_pKey, iKeyVal->first.pItem));
2123 // done now if the section is not empty or we are not pruning away
2124 // the empty sections. Otherwise let it fall through into the section
2125 // deletion code
2126 if (!a_bRemoveEmpty || !iSection->second.empty()) {
2127 return true;
2130 else {
2131 // delete all copied strings from this section. The actual
2132 // entries will be removed when the section is removed.
2133 typename TKeyVal::iterator iKeyVal = iSection->second.begin();
2134 for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
2135 DeleteString(iKeyVal->first.pItem);
2136 DeleteString(iKeyVal->second);
2140 // delete the section itself
2141 DeleteString(iSection->first.pItem);
2142 m_data.erase(iSection);
2144 return true;
2147 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2148 void
2149 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString(
2150 const SI_CHAR * a_pString
2153 // strings may exist either inside the data block, or they will be
2154 // individually allocated and stored in m_strings. We only physically
2155 // delete those stored in m_strings.
2156 if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
2157 typename TNamesDepend::iterator i = m_strings.begin();
2158 for (;i != m_strings.end(); ++i) {
2159 if (a_pString == i->pItem) {
2160 delete[] const_cast<SI_CHAR*>(i->pItem);
2161 m_strings.erase(i);
2162 break;
2168 // ---------------------------------------------------------------------------
2169 // CONVERSION FUNCTIONS
2170 // ---------------------------------------------------------------------------
2172 // Defines the conversion classes for different libraries. Before including
2173 // SimpleIni.h, set the converter that you wish you use by defining one of the
2174 // following symbols.
2176 // SI_CONVERT_GENERIC Use the Unicode reference conversion library in
2177 // the accompanying files ConvertUTF.h/c
2178 // SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
2179 // ICU headers on include path and icuuc.lib
2180 // SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
2182 #if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
2183 # ifdef _WIN32
2184 # define SI_CONVERT_WIN32
2185 # else
2186 # define SI_CONVERT_GENERIC
2187 # endif
2188 #endif
2191 * Generic case-sensitive less than comparison. This class returns numerically
2192 * ordered ASCII case-sensitive text for all possible sizes and types of
2193 * SI_CHAR.
2195 template<class SI_CHAR>
2196 struct SI_GenericCase {
2197 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2198 long cmp;
2199 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2200 cmp = (long) *pLeft - (long) *pRight;
2201 if (cmp != 0) {
2202 return cmp < 0;
2205 return *pRight != 0;
2210 * Generic ASCII case-insensitive less than comparison. This class returns
2211 * numerically ordered ASCII case-insensitive text for all possible sizes
2212 * and types of SI_CHAR. It is not safe for MBCS text comparison where
2213 * ASCII A-Z characters are used in the encoding of multi-byte characters.
2215 template<class SI_CHAR>
2216 struct SI_GenericNoCase {
2217 inline SI_CHAR locase(SI_CHAR ch) const {
2218 return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
2220 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2221 long cmp;
2222 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2223 cmp = (long) locase(*pLeft) - (long) locase(*pRight);
2224 if (cmp != 0) {
2225 return cmp < 0;
2228 return *pRight != 0;
2233 * Null conversion class for MBCS/UTF-8 to char (or equivalent).
2235 template<class SI_CHAR>
2236 class SI_ConvertA {
2237 bool m_bStoreIsUtf8;
2238 protected:
2239 SI_ConvertA() { }
2240 public:
2241 SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
2243 /* copy and assignment */
2244 SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }
2245 SI_ConvertA & operator=(const SI_ConvertA & rhs) {
2246 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2247 return *this;
2250 /** Calculate the number of SI_CHAR required for converting the input
2251 * from the storage format. The storage format is always UTF-8 or MBCS.
2253 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2254 * @param a_uInputDataLen Length of storage format data in bytes. This
2255 * must be the actual length of the data, including
2256 * NULL byte if NULL terminated string is required.
2257 * @return Number of SI_CHAR required by the string when
2258 * converted. If there are embedded NULL bytes in the
2259 * input data, only the string up and not including
2260 * the NULL byte will be converted.
2261 * @return -1 cast to size_t on a conversion error.
2263 size_t SizeFromStore(
2264 const char * a_pInputData,
2265 size_t a_uInputDataLen)
2267 (void)a_pInputData;
2268 SI_ASSERT(a_uInputDataLen != (size_t) -1);
2270 // ASCII/MBCS/UTF-8 needs no conversion
2271 return a_uInputDataLen;
2274 /** Convert the input string from the storage format to SI_CHAR.
2275 * The storage format is always UTF-8 or MBCS.
2277 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2278 * @param a_uInputDataLen Length of storage format data in bytes. This
2279 * must be the actual length of the data, including
2280 * NULL byte if NULL terminated string is required.
2281 * @param a_pOutputData Pointer to the output buffer to received the
2282 * converted data.
2283 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2284 * @return true if all of the input data was successfully
2285 * converted.
2287 bool ConvertFromStore(
2288 const char * a_pInputData,
2289 size_t a_uInputDataLen,
2290 SI_CHAR * a_pOutputData,
2291 size_t a_uOutputDataSize)
2293 // ASCII/MBCS/UTF-8 needs no conversion
2294 if (a_uInputDataLen > a_uOutputDataSize) {
2295 return false;
2297 memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
2298 return true;
2301 /** Calculate the number of char required by the storage format of this
2302 * data. The storage format is always UTF-8 or MBCS.
2304 * @param a_pInputData NULL terminated string to calculate the number of
2305 * bytes required to be converted to storage format.
2306 * @return Number of bytes required by the string when
2307 * converted to storage format. This size always
2308 * includes space for the terminating NULL character.
2309 * @return -1 cast to size_t on a conversion error.
2311 size_t SizeToStore(
2312 const SI_CHAR * a_pInputData)
2314 // ASCII/MBCS/UTF-8 needs no conversion
2315 return strlen((const char *)a_pInputData) + 1;
2318 /** Convert the input string to the storage format of this data.
2319 * The storage format is always UTF-8 or MBCS.
2321 * @param a_pInputData NULL terminated source string to convert. All of
2322 * the data will be converted including the
2323 * terminating NULL character.
2324 * @param a_pOutputData Pointer to the buffer to receive the converted
2325 * string.
2326 * @param a_uOutputDataSize Size of the output buffer in char.
2327 * @return true if all of the input data, including the
2328 * terminating NULL character was successfully
2329 * converted.
2331 bool ConvertToStore(
2332 const SI_CHAR * a_pInputData,
2333 char * a_pOutputData,
2334 size_t a_uOutputDataSize)
2336 // calc input string length (SI_CHAR type and size independent)
2337 size_t uInputLen = strlen((const char *)a_pInputData) + 1;
2338 if (uInputLen > a_uOutputDataSize) {
2339 return false;
2342 // ascii/UTF-8 needs no conversion
2343 memcpy(a_pOutputData, a_pInputData, uInputLen);
2344 return true;
2349 // ---------------------------------------------------------------------------
2350 // SI_CONVERT_GENERIC
2351 // ---------------------------------------------------------------------------
2352 #ifdef SI_CONVERT_GENERIC
2354 #define SI_Case SI_GenericCase
2355 #define SI_NoCase SI_GenericNoCase
2357 #include <wchar.h>
2358 #include "ConvertUTF.h"
2361 * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference
2362 * library functions. This can be used on all platforms.
2364 template<class SI_CHAR>
2365 class SI_ConvertW {
2366 bool m_bStoreIsUtf8;
2367 protected:
2368 SI_ConvertW() { }
2369 public:
2370 SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
2372 /* copy and assignment */
2373 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
2374 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
2375 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2376 return *this;
2379 /** Calculate the number of SI_CHAR required for converting the input
2380 * from the storage format. The storage format is always UTF-8 or MBCS.
2382 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2383 * @param a_uInputDataLen Length of storage format data in bytes. This
2384 * must be the actual length of the data, including
2385 * NULL byte if NULL terminated string is required.
2386 * @return Number of SI_CHAR required by the string when
2387 * converted. If there are embedded NULL bytes in the
2388 * input data, only the string up and not including
2389 * the NULL byte will be converted.
2390 * @return -1 cast to size_t on a conversion error.
2392 size_t SizeFromStore(
2393 const char * a_pInputData,
2394 size_t a_uInputDataLen)
2396 SI_ASSERT(a_uInputDataLen != (size_t) -1);
2398 if (m_bStoreIsUtf8) {
2399 // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
2400 // so we just return the same number of characters required as for
2401 // the source text.
2402 return a_uInputDataLen;
2404 else {
2405 return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
2409 /** Convert the input string from the storage format to SI_CHAR.
2410 * The storage format is always UTF-8 or MBCS.
2412 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2413 * @param a_uInputDataLen Length of storage format data in bytes. This
2414 * must be the actual length of the data, including
2415 * NULL byte if NULL terminated string is required.
2416 * @param a_pOutputData Pointer to the output buffer to received the
2417 * converted data.
2418 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2419 * @return true if all of the input data was successfully
2420 * converted.
2422 bool ConvertFromStore(
2423 const char * a_pInputData,
2424 size_t a_uInputDataLen,
2425 SI_CHAR * a_pOutputData,
2426 size_t a_uOutputDataSize)
2428 if (m_bStoreIsUtf8) {
2429 // This uses the Unicode reference implementation to do the
2430 // conversion from UTF-8 to wchar_t. The required files are
2431 // ConvertUTF.h and ConvertUTF.c which should be included in
2432 // the distribution but are publically available from unicode.org
2433 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
2434 ConversionResult retval;
2435 const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
2436 if (sizeof(wchar_t) == sizeof(UTF32)) {
2437 UTF32 * pUtf32 = (UTF32 *) a_pOutputData;
2438 retval = ConvertUTF8toUTF32(
2439 &pUtf8, pUtf8 + a_uInputDataLen,
2440 &pUtf32, pUtf32 + a_uOutputDataSize,
2441 lenientConversion);
2443 else if (sizeof(wchar_t) == sizeof(UTF16)) {
2444 UTF16 * pUtf16 = (UTF16 *) a_pOutputData;
2445 retval = ConvertUTF8toUTF16(
2446 &pUtf8, pUtf8 + a_uInputDataLen,
2447 &pUtf16, pUtf16 + a_uOutputDataSize,
2448 lenientConversion);
2450 return retval == conversionOK;
2452 else {
2453 size_t retval = mbstowcs(a_pOutputData,
2454 a_pInputData, a_uOutputDataSize);
2455 return retval != (size_t)(-1);
2459 /** Calculate the number of char required by the storage format of this
2460 * data. The storage format is always UTF-8 or MBCS.
2462 * @param a_pInputData NULL terminated string to calculate the number of
2463 * bytes required to be converted to storage format.
2464 * @return Number of bytes required by the string when
2465 * converted to storage format. This size always
2466 * includes space for the terminating NULL character.
2467 * @return -1 cast to size_t on a conversion error.
2469 size_t SizeToStore(
2470 const SI_CHAR * a_pInputData)
2472 if (m_bStoreIsUtf8) {
2473 // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
2474 size_t uLen = 0;
2475 while (a_pInputData[uLen]) {
2476 ++uLen;
2478 return (6 * uLen) + 1;
2480 else {
2481 size_t uLen = wcstombs(NULL, a_pInputData, 0);
2482 if (uLen == (size_t)(-1)) {
2483 return uLen;
2485 return uLen + 1; // include NULL terminator
2489 /** Convert the input string to the storage format of this data.
2490 * The storage format is always UTF-8 or MBCS.
2492 * @param a_pInputData NULL terminated source string to convert. All of
2493 * the data will be converted including the
2494 * terminating NULL character.
2495 * @param a_pOutputData Pointer to the buffer to receive the converted
2496 * string.
2497 * @param a_uOutputDataSize Size of the output buffer in char.
2498 * @return true if all of the input data, including the
2499 * terminating NULL character was successfully
2500 * converted.
2502 bool ConvertToStore(
2503 const SI_CHAR * a_pInputData,
2504 char * a_pOutputData,
2505 size_t a_uOutputDataSize
2508 if (m_bStoreIsUtf8) {
2509 // calc input string length (SI_CHAR type and size independent)
2510 size_t uInputLen = 0;
2511 while (a_pInputData[uInputLen]) {
2512 ++uInputLen;
2514 ++uInputLen; // include the NULL char
2516 // This uses the Unicode reference implementation to do the
2517 // conversion from wchar_t to UTF-8. The required files are
2518 // ConvertUTF.h and ConvertUTF.c which should be included in
2519 // the distribution but are publically available from unicode.org
2520 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
2521 ConversionResult retval;
2522 UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
2523 if (sizeof(wchar_t) == sizeof(UTF32)) {
2524 const UTF32 * pUtf32 = (const UTF32 *) a_pInputData;
2525 retval = ConvertUTF32toUTF8(
2526 &pUtf32, pUtf32 + uInputLen + 1,
2527 &pUtf8, pUtf8 + a_uOutputDataSize,
2528 lenientConversion);
2530 else if (sizeof(wchar_t) == sizeof(UTF16)) {
2531 const UTF16 * pUtf16 = (const UTF16 *) a_pInputData;
2532 retval = ConvertUTF16toUTF8(
2533 &pUtf16, pUtf16 + uInputLen + 1,
2534 &pUtf8, pUtf8 + a_uOutputDataSize,
2535 lenientConversion);
2537 return retval == conversionOK;
2539 else {
2540 size_t retval = wcstombs(a_pOutputData,
2541 a_pInputData, a_uOutputDataSize);
2542 return retval != (size_t) -1;
2547 #endif // SI_CONVERT_GENERIC
2550 // ---------------------------------------------------------------------------
2551 // SI_CONVERT_ICU
2552 // ---------------------------------------------------------------------------
2553 #ifdef SI_CONVERT_ICU
2555 #define SI_Case SI_GenericCase
2556 #define SI_NoCase SI_GenericNoCase
2558 #include <unicode/ucnv.h>
2561 * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms.
2563 template<class SI_CHAR>
2564 class SI_ConvertW {
2565 const char * m_pEncoding;
2566 UConverter * m_pConverter;
2567 protected:
2568 SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
2569 public:
2570 SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
2571 m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
2574 /* copy and assignment */
2575 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
2576 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
2577 m_pEncoding = rhs.m_pEncoding;
2578 m_pConverter = NULL;
2579 return *this;
2581 ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); }
2583 /** Calculate the number of UChar required for converting the input
2584 * from the storage format. The storage format is always UTF-8 or MBCS.
2586 * @param a_pInputData Data in storage format to be converted to UChar.
2587 * @param a_uInputDataLen Length of storage format data in bytes. This
2588 * must be the actual length of the data, including
2589 * NULL byte if NULL terminated string is required.
2590 * @return Number of UChar required by the string when
2591 * converted. If there are embedded NULL bytes in the
2592 * input data, only the string up and not including
2593 * the NULL byte will be converted.
2594 * @return -1 cast to size_t on a conversion error.
2596 size_t SizeFromStore(
2597 const char * a_pInputData,
2598 size_t a_uInputDataLen)
2600 SI_ASSERT(a_uInputDataLen != (size_t) -1);
2602 UErrorCode nError;
2604 if (!m_pConverter) {
2605 nError = U_ZERO_ERROR;
2606 m_pConverter = ucnv_open(m_pEncoding, &nError);
2607 if (U_FAILURE(nError)) {
2608 return (size_t) -1;
2612 nError = U_ZERO_ERROR;
2613 ucnv_resetToUnicode(m_pConverter);
2614 int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,
2615 a_pInputData, (int32_t) a_uInputDataLen, &nError);
2616 if (nError != U_BUFFER_OVERFLOW_ERROR) {
2617 return (size_t) -1;
2620 return (size_t) nLen;
2623 /** Convert the input string from the storage format to UChar.
2624 * The storage format is always UTF-8 or MBCS.
2626 * @param a_pInputData Data in storage format to be converted to UChar.
2627 * @param a_uInputDataLen Length of storage format data in bytes. This
2628 * must be the actual length of the data, including
2629 * NULL byte if NULL terminated string is required.
2630 * @param a_pOutputData Pointer to the output buffer to received the
2631 * converted data.
2632 * @param a_uOutputDataSize Size of the output buffer in UChar.
2633 * @return true if all of the input data was successfully
2634 * converted.
2636 bool ConvertFromStore(
2637 const char * a_pInputData,
2638 size_t a_uInputDataLen,
2639 UChar * a_pOutputData,
2640 size_t a_uOutputDataSize)
2642 UErrorCode nError;
2644 if (!m_pConverter) {
2645 nError = U_ZERO_ERROR;
2646 m_pConverter = ucnv_open(m_pEncoding, &nError);
2647 if (U_FAILURE(nError)) {
2648 return false;
2652 nError = U_ZERO_ERROR;
2653 ucnv_resetToUnicode(m_pConverter);
2654 ucnv_toUChars(m_pConverter,
2655 a_pOutputData, (int32_t) a_uOutputDataSize,
2656 a_pInputData, (int32_t) a_uInputDataLen, &nError);
2657 if (U_FAILURE(nError)) {
2658 return false;
2661 return true;
2664 /** Calculate the number of char required by the storage format of this
2665 * data. The storage format is always UTF-8 or MBCS.
2667 * @param a_pInputData NULL terminated string to calculate the number of
2668 * bytes required to be converted to storage format.
2669 * @return Number of bytes required by the string when
2670 * converted to storage format. This size always
2671 * includes space for the terminating NULL character.
2672 * @return -1 cast to size_t on a conversion error.
2674 size_t SizeToStore(
2675 const UChar * a_pInputData)
2677 UErrorCode nError;
2679 if (!m_pConverter) {
2680 nError = U_ZERO_ERROR;
2681 m_pConverter = ucnv_open(m_pEncoding, &nError);
2682 if (U_FAILURE(nError)) {
2683 return (size_t) -1;
2687 nError = U_ZERO_ERROR;
2688 ucnv_resetFromUnicode(m_pConverter);
2689 int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,
2690 a_pInputData, -1, &nError);
2691 if (nError != U_BUFFER_OVERFLOW_ERROR) {
2692 return (size_t) -1;
2695 return (size_t) nLen + 1;
2698 /** Convert the input string to the storage format of this data.
2699 * The storage format is always UTF-8 or MBCS.
2701 * @param a_pInputData NULL terminated source string to convert. All of
2702 * the data will be converted including the
2703 * terminating NULL character.
2704 * @param a_pOutputData Pointer to the buffer to receive the converted
2705 * string.
2706 * @param a_pOutputDataSize Size of the output buffer in char.
2707 * @return true if all of the input data, including the
2708 * terminating NULL character was successfully
2709 * converted.
2711 bool ConvertToStore(
2712 const UChar * a_pInputData,
2713 char * a_pOutputData,
2714 size_t a_uOutputDataSize)
2716 UErrorCode nError;
2718 if (!m_pConverter) {
2719 nError = U_ZERO_ERROR;
2720 m_pConverter = ucnv_open(m_pEncoding, &nError);
2721 if (U_FAILURE(nError)) {
2722 return false;
2726 nError = U_ZERO_ERROR;
2727 ucnv_resetFromUnicode(m_pConverter);
2728 ucnv_fromUChars(m_pConverter,
2729 a_pOutputData, (int32_t) a_uOutputDataSize,
2730 a_pInputData, -1, &nError);
2731 if (U_FAILURE(nError)) {
2732 return false;
2735 return true;
2739 #endif // SI_CONVERT_ICU
2742 // ---------------------------------------------------------------------------
2743 // SI_CONVERT_WIN32
2744 // ---------------------------------------------------------------------------
2745 #ifdef SI_CONVERT_WIN32
2747 #define SI_Case SI_GenericCase
2749 // Windows CE doesn't have errno or MBCS libraries
2750 #ifdef _WIN32_WCE
2751 # ifndef SI_NO_MBCS
2752 # define SI_NO_MBCS
2753 # endif
2754 #endif
2756 #include <windows.h>
2757 #ifdef SI_NO_MBCS
2758 # define SI_NoCase SI_GenericNoCase
2759 #else // !SI_NO_MBCS
2761 * Case-insensitive comparison class using Win32 MBCS functions. This class
2762 * returns a case-insensitive semi-collation order for MBCS text. It may not
2763 * be safe for UTF-8 text returned in char format as we don't know what
2764 * characters will be folded by the function! Therefore, if you are using
2765 * SI_CHAR == char and SetUnicode(true), then you need to use the generic
2766 * SI_NoCase class instead.
2768 #include <mbstring.h>
2769 template<class SI_CHAR>
2770 struct SI_NoCase {
2771 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2772 if (sizeof(SI_CHAR) == sizeof(char)) {
2773 return _mbsicmp((const unsigned char *)pLeft,
2774 (const unsigned char *)pRight) < 0;
2776 if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
2777 return _wcsicmp((const wchar_t *)pLeft,
2778 (const wchar_t *)pRight) < 0;
2780 return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight);
2783 #endif // SI_NO_MBCS
2786 * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses
2787 * only the Win32 functions and doesn't require the external Unicode UTF-8
2788 * conversion library. It will not work on Windows 95 without using Microsoft
2789 * Layer for Unicode in your application.
2791 template<class SI_CHAR>
2792 class SI_ConvertW {
2793 UINT m_uCodePage;
2794 protected:
2795 SI_ConvertW() { }
2796 public:
2797 SI_ConvertW(bool a_bStoreIsUtf8) {
2798 m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP;
2801 /* copy and assignment */
2802 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
2803 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
2804 m_uCodePage = rhs.m_uCodePage;
2805 return *this;
2808 /** Calculate the number of SI_CHAR required for converting the input
2809 * from the storage format. The storage format is always UTF-8 or MBCS.
2811 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2812 * @param a_uInputDataLen Length of storage format data in bytes. This
2813 * must be the actual length of the data, including
2814 * NULL byte if NULL terminated string is required.
2815 * @return Number of SI_CHAR required by the string when
2816 * converted. If there are embedded NULL bytes in the
2817 * input data, only the string up and not including
2818 * the NULL byte will be converted.
2819 * @return -1 cast to size_t on a conversion error.
2821 size_t SizeFromStore(
2822 const char * a_pInputData,
2823 size_t a_uInputDataLen)
2825 SI_ASSERT(a_uInputDataLen != (size_t) -1);
2827 int retval = MultiByteToWideChar(
2828 m_uCodePage, 0,
2829 a_pInputData, (int) a_uInputDataLen,
2830 0, 0);
2831 return (size_t)(retval > 0 ? retval : -1);
2834 /** Convert the input string from the storage format to SI_CHAR.
2835 * The storage format is always UTF-8 or MBCS.
2837 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2838 * @param a_uInputDataLen Length of storage format data in bytes. This
2839 * must be the actual length of the data, including
2840 * NULL byte if NULL terminated string is required.
2841 * @param a_pOutputData Pointer to the output buffer to received the
2842 * converted data.
2843 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2844 * @return true if all of the input data was successfully
2845 * converted.
2847 bool ConvertFromStore(
2848 const char * a_pInputData,
2849 size_t a_uInputDataLen,
2850 SI_CHAR * a_pOutputData,
2851 size_t a_uOutputDataSize)
2853 int nSize = MultiByteToWideChar(
2854 m_uCodePage, 0,
2855 a_pInputData, (int) a_uInputDataLen,
2856 (wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
2857 return (nSize > 0);
2860 /** Calculate the number of char required by the storage format of this
2861 * data. The storage format is always UTF-8.
2863 * @param a_pInputData NULL terminated string to calculate the number of
2864 * bytes required to be converted to storage format.
2865 * @return Number of bytes required by the string when
2866 * converted to storage format. This size always
2867 * includes space for the terminating NULL character.
2868 * @return -1 cast to size_t on a conversion error.
2870 size_t SizeToStore(
2871 const SI_CHAR * a_pInputData)
2873 int retval = WideCharToMultiByte(
2874 m_uCodePage, 0,
2875 (const wchar_t *) a_pInputData, -1,
2876 0, 0, 0, 0);
2877 return (size_t) (retval > 0 ? retval : -1);
2880 /** Convert the input string to the storage format of this data.
2881 * The storage format is always UTF-8 or MBCS.
2883 * @param a_pInputData NULL terminated source string to convert. All of
2884 * the data will be converted including the
2885 * terminating NULL character.
2886 * @param a_pOutputData Pointer to the buffer to receive the converted
2887 * string.
2888 * @param a_pOutputDataSize Size of the output buffer in char.
2889 * @return true if all of the input data, including the
2890 * terminating NULL character was successfully
2891 * converted.
2893 bool ConvertToStore(
2894 const SI_CHAR * a_pInputData,
2895 char * a_pOutputData,
2896 size_t a_uOutputDataSize)
2898 int retval = WideCharToMultiByte(
2899 m_uCodePage, 0,
2900 (const wchar_t *) a_pInputData, -1,
2901 a_pOutputData, (int) a_uOutputDataSize, 0, 0);
2902 return retval > 0;
2906 #endif // SI_CONVERT_WIN32
2909 // ---------------------------------------------------------------------------
2910 // TYPE DEFINITIONS
2911 // ---------------------------------------------------------------------------
2913 typedef CSimpleIniTempl<char,
2914 SI_NoCase<char>,SI_ConvertA<char> > CSimpleIniA;
2915 typedef CSimpleIniTempl<char,
2916 SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA;
2918 #if defined(SI_CONVERT_ICU)
2919 typedef CSimpleIniTempl<UChar,
2920 SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;
2921 typedef CSimpleIniTempl<UChar,
2922 SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;
2923 #else
2924 typedef CSimpleIniTempl<wchar_t,
2925 SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;
2926 typedef CSimpleIniTempl<wchar_t,
2927 SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;
2928 #endif
2930 #ifdef _UNICODE
2931 # define CSimpleIni CSimpleIniW
2932 # define CSimpleIniCase CSimpleIniCaseW
2933 # define SI_NEWLINE SI_NEWLINE_W
2934 #else // !_UNICODE
2935 # define CSimpleIni CSimpleIniA
2936 # define CSimpleIniCase CSimpleIniCaseA
2937 # define SI_NEWLINE SI_NEWLINE_A
2938 #endif // _UNICODE
2940 #ifdef _MSC_VER
2941 # pragma warning (pop)
2942 #endif
2944 #endif // INCLUDED_SimpleIni_h