moved checkInteractions from InteractionsParser to Lattice
[cluster_expansion.git] / SimpleIni.h
blob26a4ebc4a866c29b2eacd59837bbfd3afa66a41b
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;
1040 public:
1041 static std::string str_SI_Error(SI_Error rc)
1043 switch (rc) {
1044 case SI_OK: return std::string("no error");
1045 case SI_UPDATED: return std::string("existing value updated");
1046 case SI_INSERTED: return std::string("new value inserted");
1047 case SI_FAIL: return std::string("generic failure");
1048 case SI_NOMEM: return std::string("out of memory");
1049 case SI_FILE: return std::string("file error");
1050 default: return std::string("(invalid error)");
1056 // ---------------------------------------------------------------------------
1057 // IMPLEMENTATION
1058 // ---------------------------------------------------------------------------
1060 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1061 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl(
1062 bool a_bIsUtf8,
1063 bool a_bAllowMultiKey,
1064 bool a_bAllowMultiLine
1066 : m_pData(0)
1067 , m_uDataLen(0)
1068 , m_pFileComment(NULL)
1069 , m_bStoreIsUtf8(a_bIsUtf8)
1070 , m_bAllowMultiKey(a_bAllowMultiKey)
1071 , m_bAllowMultiLine(a_bAllowMultiLine)
1072 , m_nOrder(0)
1075 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1076 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::~CSimpleIniTempl()
1078 Reset();
1081 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1082 void
1083 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Reset()
1085 // remove all data
1086 delete[] m_pData;
1087 m_pData = NULL;
1088 m_uDataLen = 0;
1089 m_pFileComment = NULL;
1090 if (!m_data.empty()) {
1091 m_data.erase(m_data.begin(), m_data.end());
1094 // remove all strings
1095 if (!m_strings.empty()) {
1096 typename TNamesDepend::iterator i = m_strings.begin();
1097 for (; i != m_strings.end(); ++i) {
1098 delete[] const_cast<SI_CHAR*>(i->pItem);
1100 m_strings.erase(m_strings.begin(), m_strings.end());
1104 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1105 SI_Error
1106 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
1107 const char * a_pszFile
1110 FILE * fp = NULL;
1111 #if __STDC_WANT_SECURE_LIB__
1112 fopen_s(&fp, a_pszFile, "rb");
1113 #else
1114 fp = fopen(a_pszFile, "rb");
1115 #endif
1116 if (!fp) {
1117 return SI_FILE;
1119 SI_Error rc = LoadFile(fp);
1120 fclose(fp);
1121 return rc;
1124 #ifdef SI_HAS_WIDE_FILE
1125 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1126 SI_Error
1127 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
1128 const SI_WCHAR_T * a_pwszFile
1131 #ifdef _WIN32
1132 FILE * fp = NULL;
1133 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1134 _wfopen_s(&fp, a_pwszFile, L"rb");
1135 #else
1136 fp = _wfopen(a_pwszFile, L"rb");
1137 #endif
1138 if (!fp) return SI_FILE;
1139 SI_Error rc = LoadFile(fp);
1140 fclose(fp);
1141 return rc;
1142 #else // SI_CONVERT_ICU
1143 char szFile[256];
1144 u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
1145 return LoadFile(szFile);
1146 #endif
1148 #endif // SI_HAS_WIDE_FILE
1150 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1151 SI_Error
1152 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
1153 FILE * a_fpFile
1156 // load the raw file data
1157 int retval = fseek(a_fpFile, 0, SEEK_END);
1158 if (retval != 0) {
1159 return SI_FILE;
1161 long lSize = ftell(a_fpFile);
1162 if (lSize < 0) {
1163 return SI_FILE;
1165 if (lSize == 0) {
1166 return SI_OK;
1168 char * pData = new char[lSize];
1169 if (!pData) {
1170 return SI_NOMEM;
1172 fseek(a_fpFile, 0, SEEK_SET);
1173 size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
1174 if (uRead != (size_t) lSize) {
1175 delete[] pData;
1176 return SI_FILE;
1179 // convert the raw data to unicode
1180 SI_Error rc = Load(pData, uRead);
1181 delete[] pData;
1182 return rc;
1185 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1186 SI_Error
1187 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Load(
1188 const char * a_pData,
1189 size_t a_uDataLen
1192 SI_CONVERTER converter(m_bStoreIsUtf8);
1194 if (a_uDataLen == 0) {
1195 return SI_OK;
1198 // consume the UTF-8 BOM if it exists
1199 if (m_bStoreIsUtf8 && a_uDataLen >= 3) {
1200 if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
1201 a_pData += 3;
1202 a_uDataLen -= 3;
1206 // determine the length of the converted data
1207 size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
1208 if (uLen == (size_t)(-1)) {
1209 return SI_FAIL;
1212 // allocate memory for the data, ensure that there is a NULL
1213 // terminator wherever the converted data ends
1214 SI_CHAR * pData = new SI_CHAR[uLen+1];
1215 if (!pData) {
1216 return SI_NOMEM;
1218 memset(pData, 0, sizeof(SI_CHAR)*(uLen+1));
1220 // convert the data
1221 if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
1222 delete[] pData;
1223 return SI_FAIL;
1226 // parse it
1227 const static SI_CHAR empty = 0;
1228 SI_CHAR * pWork = pData;
1229 const SI_CHAR * pSection = &empty;
1230 const SI_CHAR * pItem = NULL;
1231 const SI_CHAR * pVal = NULL;
1232 const SI_CHAR * pComment = NULL;
1234 // We copy the strings if we are loading data into this class when we
1235 // already have stored some.
1236 bool bCopyStrings = (m_pData != NULL);
1238 // find a file comment if it exists, this is a comment that starts at the
1239 // beginning of the file and continues until the first blank line.
1240 SI_Error rc = FindFileComment(pWork, bCopyStrings);
1241 if (rc < 0) return rc;
1243 // add every entry in the file to the data table
1244 while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
1245 rc = AddEntry(pSection, pItem, pVal, pComment, bCopyStrings);
1246 if (rc < 0) return rc;
1249 // store these strings if we didn't copy them
1250 if (bCopyStrings) {
1251 delete[] pData;
1253 else {
1254 m_pData = pData;
1255 m_uDataLen = uLen+1;
1258 return SI_OK;
1261 #ifdef SI_SUPPORT_IOSTREAMS
1262 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1263 SI_Error
1264 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Load(
1265 std::istream & a_istream
1268 std::string strData;
1269 char szBuf[512];
1270 do {
1271 a_istream.get(szBuf, sizeof(szBuf), '\0');
1272 strData.append(szBuf);
1274 while (a_istream.good());
1275 return Load(strData);
1277 #endif // SI_SUPPORT_IOSTREAMS
1279 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1280 SI_Error
1281 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindFileComment(
1282 SI_CHAR *& a_pData,
1283 bool a_bCopyStrings
1286 // there can only be a single file comment
1287 if (m_pFileComment) {
1288 return SI_OK;
1291 // Load the file comment as multi-line text, this will modify all of
1292 // the newline characters to be single \n chars
1293 if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
1294 return SI_OK;
1297 // copy the string if necessary
1298 if (a_bCopyStrings) {
1299 SI_Error rc = CopyString(m_pFileComment);
1300 if (rc < 0) return rc;
1303 return SI_OK;
1306 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1307 bool
1308 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
1309 SI_CHAR *& a_pData,
1310 const SI_CHAR *& a_pSection,
1311 const SI_CHAR *& a_pKey,
1312 const SI_CHAR *& a_pVal,
1313 const SI_CHAR *& a_pComment
1314 ) const
1316 a_pComment = NULL;
1318 SI_CHAR * pTrail = NULL;
1319 while (*a_pData) {
1320 // skip spaces and empty lines
1321 while (*a_pData && IsSpace(*a_pData)) {
1322 ++a_pData;
1324 if (!*a_pData) {
1325 break;
1328 // skip processing of comment lines but keep a pointer to
1329 // the start of the comment.
1330 if (IsComment(*a_pData)) {
1331 LoadMultiLineText(a_pData, a_pComment, NULL, true);
1332 continue;
1335 // process section names
1336 if (*a_pData == '[') {
1337 // skip leading spaces
1338 ++a_pData;
1339 while (*a_pData && IsSpace(*a_pData)) {
1340 ++a_pData;
1343 // find the end of the section name (it may contain spaces)
1344 // and convert it to lowercase as necessary
1345 a_pSection = a_pData;
1346 while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
1347 ++a_pData;
1350 // if it's an invalid line, just skip it
1351 if (*a_pData != ']') {
1352 continue;
1355 // remove trailing spaces from the section
1356 pTrail = a_pData - 1;
1357 while (pTrail >= a_pSection && IsSpace(*pTrail)) {
1358 --pTrail;
1360 ++pTrail;
1361 *pTrail = 0;
1363 // skip to the end of the line
1364 ++a_pData; // safe as checked that it == ']' above
1365 while (*a_pData && !IsNewLineChar(*a_pData)) {
1366 ++a_pData;
1369 a_pKey = NULL;
1370 a_pVal = NULL;
1371 return true;
1374 // find the end of the key name (it may contain spaces)
1375 // and convert it to lowercase as necessary
1376 a_pKey = a_pData;
1377 while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
1378 ++a_pData;
1381 // if it's an invalid line, just skip it
1382 if (*a_pData != '=') {
1383 continue;
1386 // empty keys are invalid
1387 if (a_pKey == a_pData) {
1388 while (*a_pData && !IsNewLineChar(*a_pData)) {
1389 ++a_pData;
1391 continue;
1394 // remove trailing spaces from the key
1395 pTrail = a_pData - 1;
1396 while (pTrail >= a_pKey && IsSpace(*pTrail)) {
1397 --pTrail;
1399 ++pTrail;
1400 *pTrail = 0;
1402 // skip leading whitespace on the value
1403 ++a_pData; // safe as checked that it == '=' above
1404 while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
1405 ++a_pData;
1408 // find the end of the value which is the end of this line
1409 a_pVal = a_pData;
1410 while (*a_pData && !IsNewLineChar(*a_pData)) {
1411 ++a_pData;
1414 // remove trailing spaces from the value
1415 pTrail = a_pData - 1;
1416 if (*a_pData) { // prepare for the next round
1417 SkipNewLine(a_pData);
1419 while (pTrail >= a_pVal && IsSpace(*pTrail)) {
1420 --pTrail;
1422 ++pTrail;
1423 *pTrail = 0;
1425 // check for multi-line entries
1426 if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
1427 // skip the "<<<" to get the tag that will end the multiline
1428 const SI_CHAR * pTagName = a_pVal + 3;
1429 return LoadMultiLineText(a_pData, a_pVal, pTagName);
1432 // return the standard entry
1433 return true;
1436 return false;
1439 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1440 bool
1441 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineTag(
1442 const SI_CHAR * a_pVal
1443 ) const
1445 // check for the "<<<" prefix for a multi-line entry
1446 if (*a_pVal++ != '<') return false;
1447 if (*a_pVal++ != '<') return false;
1448 if (*a_pVal++ != '<') return false;
1449 return true;
1452 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1453 bool
1454 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData(
1455 const SI_CHAR * a_pData
1456 ) const
1458 // data is multi-line if it has any of the following features:
1459 // * whitespace prefix
1460 // * embedded newlines
1461 // * whitespace suffix
1463 // empty string
1464 if (!*a_pData) {
1465 return false;
1468 // check for prefix
1469 if (IsSpace(*a_pData)) {
1470 return true;
1473 // embedded newlines
1474 while (*a_pData) {
1475 if (IsNewLineChar(*a_pData)) {
1476 return true;
1478 ++a_pData;
1481 // check for suffix
1482 if (IsSpace(*--a_pData)) {
1483 return true;
1486 return false;
1489 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1490 bool
1491 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar(
1492 SI_CHAR a_c
1493 ) const
1495 return (a_c == '\n' || a_c == '\r');
1498 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1499 bool
1500 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
1501 SI_CHAR *& a_pData,
1502 const SI_CHAR *& a_pVal,
1503 const SI_CHAR * a_pTagName,
1504 bool a_bAllowBlankLinesInComment
1505 ) const
1507 // we modify this data to strip all newlines down to a single '\n'
1508 // character. This means that on Windows we need to strip out some
1509 // characters which will make the data shorter.
1510 // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
1511 // LINE1-LINE1\nLINE2-LINE2\0
1512 // The pDataLine entry is the pointer to the location in memory that
1513 // the current line needs to start to run following the existing one.
1514 // This may be the same as pCurrLine in which case no move is needed.
1515 SI_CHAR * pDataLine = a_pData;
1516 SI_CHAR * pCurrLine;
1518 // value starts at the current line
1519 a_pVal = a_pData;
1521 // find the end tag. This tag must start in column 1 and be
1522 // followed by a newline. No whitespace removal is done while
1523 // searching for this tag.
1524 SI_CHAR cEndOfLineChar = *a_pData;
1525 for(;;) {
1526 // if we are loading comments then we need a comment character as
1527 // the first character on every line
1528 if (!a_pTagName && !IsComment(*a_pData)) {
1529 // if we aren't allowing blank lines then we're done
1530 if (!a_bAllowBlankLinesInComment) {
1531 break;
1534 // if we are allowing blank lines then we only include them
1535 // in this comment if another comment follows, so read ahead
1536 // to find out.
1537 SI_CHAR * pCurr = a_pData;
1538 int nNewLines = 0;
1539 while (IsSpace(*pCurr)) {
1540 if (IsNewLineChar(*pCurr)) {
1541 ++nNewLines;
1542 SkipNewLine(pCurr);
1544 else {
1545 ++pCurr;
1549 // we have a comment, add the blank lines to the output
1550 // and continue processing from here
1551 if (IsComment(*pCurr)) {
1552 for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n';
1553 a_pData = pCurr;
1554 continue;
1557 // the comment ends here
1558 break;
1561 // find the end of this line
1562 pCurrLine = a_pData;
1563 while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
1565 // move this line down to the location that it should be if necessary
1566 if (pDataLine < pCurrLine) {
1567 size_t nLen = (size_t) (a_pData - pCurrLine);
1568 memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
1569 pDataLine[nLen] = '\0';
1572 // end the line with a NULL
1573 cEndOfLineChar = *a_pData;
1574 *a_pData = 0;
1576 // if are looking for a tag then do the check now. This is done before
1577 // checking for end of the data, so that if we have the tag at the end
1578 // of the data then the tag is removed correctly.
1579 if (a_pTagName &&
1580 (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)))
1582 break;
1585 // if we are at the end of the data then we just automatically end
1586 // this entry and return the current data.
1587 if (!cEndOfLineChar) {
1588 return true;
1591 // otherwise we need to process this newline to ensure that it consists
1592 // of just a single \n character.
1593 pDataLine += (a_pData - pCurrLine);
1594 *a_pData = cEndOfLineChar;
1595 SkipNewLine(a_pData);
1596 *pDataLine++ = '\n';
1599 // if we didn't find a comment at all then return false
1600 if (a_pVal == a_pData) {
1601 a_pVal = NULL;
1602 return false;
1605 // the data (which ends at the end of the last line) needs to be
1606 // null-terminated BEFORE before the newline character(s). If the
1607 // user wants a new line in the multi-line data then they need to
1608 // add an empty line before the tag.
1609 *--pDataLine = '\0';
1611 // if looking for a tag and if we aren't at the end of the data,
1612 // then move a_pData to the start of the next line.
1613 if (a_pTagName && cEndOfLineChar) {
1614 SI_ASSERT(IsNewLineChar(cEndOfLineChar));
1615 *a_pData = cEndOfLineChar;
1616 SkipNewLine(a_pData);
1619 return true;
1622 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1623 SI_Error
1624 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString(
1625 const SI_CHAR *& a_pString
1628 size_t uLen = 0;
1629 if (sizeof(SI_CHAR) == sizeof(char)) {
1630 uLen = strlen((const char *)a_pString);
1632 else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
1633 uLen = wcslen((const wchar_t *)a_pString);
1635 else {
1636 for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
1638 ++uLen; // NULL character
1639 SI_CHAR * pCopy = new SI_CHAR[uLen];
1640 if (!pCopy) {
1641 return SI_NOMEM;
1643 memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
1644 m_strings.push_back(pCopy);
1645 a_pString = pCopy;
1646 return SI_OK;
1649 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1650 SI_Error
1651 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
1652 const SI_CHAR * a_pSection,
1653 const SI_CHAR * a_pKey,
1654 const SI_CHAR * a_pValue,
1655 const SI_CHAR * a_pComment,
1656 bool a_bCopyStrings
1659 SI_Error rc;
1660 bool bInserted = false;
1662 SI_ASSERT(!a_pComment || IsComment(*a_pComment));
1664 // if we are copying strings then make a copy of the comment now
1665 // because we will need it when we add the entry.
1666 if (a_bCopyStrings && a_pComment) {
1667 rc = CopyString(a_pComment);
1668 if (rc < 0) return rc;
1671 // check for existence of the section first if we need string copies
1672 typename TSection::iterator iSection = m_data.end();
1673 if (a_bCopyStrings) {
1674 iSection = m_data.find(a_pSection);
1675 if (iSection == m_data.end()) {
1676 // if the section doesn't exist then we need a copy as the
1677 // string needs to last beyond the end of this function
1678 // because we will be inserting the section next
1679 rc = CopyString(a_pSection);
1680 if (rc < 0) return rc;
1684 // create the section entry
1685 if (iSection == m_data.end()) {
1686 Entry oKey(a_pSection, ++m_nOrder);
1687 if (a_pComment && (!a_pKey || !a_pValue)) {
1688 oKey.pComment = a_pComment;
1690 typename TSection::value_type oEntry(oKey, TKeyVal());
1691 typedef typename TSection::iterator SectionIterator;
1692 std::pair<SectionIterator,bool> i =
1693 m_data.insert(oEntry);
1694 iSection = i.first;
1695 bInserted = true;
1697 if (!a_pKey || !a_pValue) {
1698 // section only entries are specified with pItem and pVal as NULL
1699 return bInserted ? SI_INSERTED : SI_UPDATED;
1702 // check for existence of the key
1703 TKeyVal & keyval = iSection->second;
1704 typename TKeyVal::iterator iKey = keyval.find(a_pKey);
1706 // make string copies if necessary
1707 if (a_bCopyStrings) {
1708 if (m_bAllowMultiKey || iKey == keyval.end()) {
1709 // if the key doesn't exist then we need a copy as the
1710 // string needs to last beyond the end of this function
1711 // because we will be inserting the key next
1712 rc = CopyString(a_pKey);
1713 if (rc < 0) return rc;
1716 // we always need a copy of the value
1717 rc = CopyString(a_pValue);
1718 if (rc < 0) return rc;
1721 // create the key entry
1722 if (iKey == keyval.end() || m_bAllowMultiKey) {
1723 Entry oKey(a_pKey, ++m_nOrder);
1724 if (a_pComment) {
1725 oKey.pComment = a_pComment;
1727 typename TKeyVal::value_type oEntry(oKey, NULL);
1728 iKey = keyval.insert(oEntry);
1729 bInserted = true;
1731 iKey->second = a_pValue;
1732 return bInserted ? SI_INSERTED : SI_UPDATED;
1735 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1736 const SI_CHAR *
1737 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetValue(
1738 const SI_CHAR * a_pSection,
1739 const SI_CHAR * a_pKey,
1740 const SI_CHAR * a_pDefault,
1741 bool * a_pHasMultiple
1742 ) const
1744 if (a_pHasMultiple) {
1745 *a_pHasMultiple = false;
1747 if (!a_pSection || !a_pKey) {
1748 return a_pDefault;
1750 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1751 if (iSection == m_data.end()) {
1752 return a_pDefault;
1754 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
1755 if (iKeyVal == iSection->second.end()) {
1756 return a_pDefault;
1759 // check for multiple entries with the same key
1760 if (m_bAllowMultiKey && a_pHasMultiple) {
1761 typename TKeyVal::const_iterator iTemp = iKeyVal;
1762 if (++iTemp != iSection->second.end()) {
1763 if (!IsLess(a_pKey, iTemp->first.pItem)) {
1764 *a_pHasMultiple = true;
1769 return iKeyVal->second;
1772 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1773 bool
1774 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues(
1775 const SI_CHAR * a_pSection,
1776 const SI_CHAR * a_pKey,
1777 TNamesDepend & a_values
1778 ) const
1780 if (!a_pSection || !a_pKey) {
1781 return false;
1783 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1784 if (iSection == m_data.end()) {
1785 return false;
1787 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
1788 if (iKeyVal == iSection->second.end()) {
1789 return false;
1792 // insert all values for this key
1793 a_values.push_back(iKeyVal->second);
1794 if (m_bAllowMultiKey) {
1795 ++iKeyVal;
1796 while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {
1797 a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.nOrder));
1798 ++iKeyVal;
1802 return true;
1805 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1807 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSectionSize(
1808 const SI_CHAR * a_pSection
1809 ) const
1811 if (!a_pSection) {
1812 return -1;
1815 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1816 if (iSection == m_data.end()) {
1817 return -1;
1819 const TKeyVal & section = iSection->second;
1821 // if multi-key isn't permitted then the section size is
1822 // the number of keys that we have.
1823 if (!m_bAllowMultiKey || section.empty()) {
1824 return (int) section.size();
1827 // otherwise we need to count them
1828 int nCount = 0;
1829 const SI_CHAR * pLastKey = NULL;
1830 typename TKeyVal::const_iterator iKeyVal = section.begin();
1831 for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
1832 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
1833 ++nCount;
1834 pLastKey = iKeyVal->first.pItem;
1837 return nCount;
1840 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1841 const typename CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::TKeyVal *
1842 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSection(
1843 const SI_CHAR * a_pSection
1844 ) const
1846 if (a_pSection) {
1847 typename TSection::const_iterator i = m_data.find(a_pSection);
1848 if (i != m_data.end()) {
1849 return &(i->second);
1852 return 0;
1855 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1856 void
1857 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections(
1858 TNamesDepend & a_names
1859 ) const
1861 typename TSection::const_iterator i = m_data.begin();
1862 for (int n = 0; i != m_data.end(); ++i, ++n ) {
1863 a_names.push_back(i->first);
1867 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1868 bool
1869 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllKeys(
1870 const SI_CHAR * a_pSection,
1871 TNamesDepend & a_names
1872 ) const
1874 if (!a_pSection) {
1875 return false;
1878 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1879 if (iSection == m_data.end()) {
1880 return false;
1883 const TKeyVal & section = iSection->second;
1884 const SI_CHAR * pLastKey = NULL;
1885 typename TKeyVal::const_iterator iKeyVal = section.begin();
1886 for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) {
1887 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
1888 a_names.push_back(iKeyVal->first);
1889 pLastKey = iKeyVal->first.pItem;
1893 return true;
1896 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1897 SI_Error
1898 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
1899 const char * a_pszFile,
1900 bool a_bAddSignature
1901 ) const
1903 FILE * fp = NULL;
1904 #if __STDC_WANT_SECURE_LIB__
1905 fopen_s(&fp, a_pszFile, "wb");
1906 #else
1907 fp = fopen(a_pszFile, "wb");
1908 #endif
1909 if (!fp) return SI_FILE;
1910 SI_Error rc = SaveFile(fp, a_bAddSignature);
1911 fclose(fp);
1912 return rc;
1915 #ifdef SI_HAS_WIDE_FILE
1916 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1917 SI_Error
1918 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
1919 const SI_WCHAR_T * a_pwszFile,
1920 bool a_bAddSignature
1921 ) const
1923 #ifdef _WIN32
1924 FILE * fp = _wfopen(a_pwszFile, L"wb");
1925 if (!fp) return SI_FILE;
1926 SI_Error rc = SaveFile(fp, a_bAddSignature);
1927 fclose(fp);
1928 return rc;
1929 #else // SI_CONVERT_ICU
1930 char szFile[256];
1931 u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
1932 return SaveFile(szFile, a_bAddSignature);
1933 #endif
1935 #endif // SI_HAS_WIDE_FILE
1937 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1938 SI_Error
1939 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
1940 FILE * a_pFile,
1941 bool a_bAddSignature
1942 ) const
1944 FileWriter writer(a_pFile);
1945 return Save(writer, a_bAddSignature);
1948 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1949 SI_Error
1950 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
1951 OutputWriter & a_oOutput,
1952 bool a_bAddSignature
1953 ) const
1955 Converter convert(m_bStoreIsUtf8);
1957 // add the UTF-8 signature if it is desired
1958 if (m_bStoreIsUtf8 && a_bAddSignature) {
1959 a_oOutput.Write(SI_UTF8_SIGNATURE);
1962 // get all of the sections sorted in load order
1963 TNamesDepend oSections;
1964 GetAllSections(oSections);
1965 #if defined(_MSC_VER) && _MSC_VER <= 1200
1966 oSections.sort();
1967 #else
1968 oSections.sort(typename Entry::LoadOrder());
1969 #endif
1971 // write the file comment if we have one
1972 bool bNeedNewLine = false;
1973 if (m_pFileComment) {
1974 if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
1975 return SI_FAIL;
1977 bNeedNewLine = true;
1980 // iterate through our sections and output the data
1981 typename TNamesDepend::const_iterator iSection = oSections.begin();
1982 for ( ; iSection != oSections.end(); ++iSection ) {
1983 // write out the comment if there is one
1984 if (iSection->pComment) {
1985 if (!convert.ConvertToStore(iSection->pComment)) {
1986 return SI_FAIL;
1988 if (bNeedNewLine) {
1989 a_oOutput.Write(SI_NEWLINE_A);
1990 a_oOutput.Write(SI_NEWLINE_A);
1992 a_oOutput.Write(convert.Data());
1993 a_oOutput.Write(SI_NEWLINE_A);
1994 bNeedNewLine = false;
1997 if (bNeedNewLine) {
1998 a_oOutput.Write(SI_NEWLINE_A);
1999 a_oOutput.Write(SI_NEWLINE_A);
2000 bNeedNewLine = false;
2003 // write the section (unless there is no section name)
2004 if (*iSection->pItem) {
2005 if (!convert.ConvertToStore(iSection->pItem)) {
2006 return SI_FAIL;
2008 a_oOutput.Write("[");
2009 a_oOutput.Write(convert.Data());
2010 a_oOutput.Write("]");
2011 a_oOutput.Write(SI_NEWLINE_A);
2014 // get all of the keys sorted in load order
2015 TNamesDepend oKeys;
2016 GetAllKeys(iSection->pItem, oKeys);
2017 #if defined(_MSC_VER) && _MSC_VER <= 1200
2018 oKeys.sort();
2019 #else
2020 oKeys.sort(typename Entry::LoadOrder());
2021 #endif
2023 // write all keys and values
2024 typename TNamesDepend::const_iterator iKey = oKeys.begin();
2025 for ( ; iKey != oKeys.end(); ++iKey) {
2026 // get all values for this key
2027 TNamesDepend oValues;
2028 GetAllValues(iSection->pItem, iKey->pItem, oValues);
2030 // write out the comment if there is one
2031 if (iKey->pComment) {
2032 a_oOutput.Write(SI_NEWLINE_A);
2033 if (!OutputMultiLineText(a_oOutput, convert, iKey->pComment)) {
2034 return SI_FAIL;
2038 typename TNamesDepend::const_iterator iValue = oValues.begin();
2039 for ( ; iValue != oValues.end(); ++iValue) {
2040 // write the key
2041 if (!convert.ConvertToStore(iKey->pItem)) {
2042 return SI_FAIL;
2044 a_oOutput.Write(convert.Data());
2046 // write the value
2047 if (!convert.ConvertToStore(iValue->pItem)) {
2048 return SI_FAIL;
2050 a_oOutput.Write("=");
2051 if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
2052 // multi-line data needs to be processed specially to ensure
2053 // that we use the correct newline format for the current system
2054 a_oOutput.Write("<<<SI-END-OF-MULTILINE-TEXT" SI_NEWLINE_A);
2055 if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
2056 return SI_FAIL;
2058 a_oOutput.Write("SI-END-OF-MULTILINE-TEXT");
2060 else {
2061 a_oOutput.Write(convert.Data());
2063 a_oOutput.Write(SI_NEWLINE_A);
2067 bNeedNewLine = true;
2070 return SI_OK;
2073 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2074 bool
2075 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::OutputMultiLineText(
2076 OutputWriter & a_oOutput,
2077 Converter & a_oConverter,
2078 const SI_CHAR * a_pText
2079 ) const
2081 const SI_CHAR * pEndOfLine;
2082 SI_CHAR cEndOfLineChar = *a_pText;
2083 while (cEndOfLineChar) {
2084 // find the end of this line
2085 pEndOfLine = a_pText;
2086 for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;
2087 cEndOfLineChar = *pEndOfLine;
2089 // temporarily null terminate, convert and output the line
2090 *const_cast<SI_CHAR*>(pEndOfLine) = 0;
2091 if (!a_oConverter.ConvertToStore(a_pText)) {
2092 return false;
2094 *const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar;
2095 a_pText += (pEndOfLine - a_pText) + 1;
2096 a_oOutput.Write(a_oConverter.Data());
2097 a_oOutput.Write(SI_NEWLINE_A);
2099 return true;
2102 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2103 bool
2104 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(
2105 const SI_CHAR * a_pSection,
2106 const SI_CHAR * a_pKey,
2107 bool a_bRemoveEmpty
2110 if (!a_pSection) {
2111 return false;
2114 typename TSection::iterator iSection = m_data.find(a_pSection);
2115 if (iSection == m_data.end()) {
2116 return false;
2119 // remove a single key if we have a keyname
2120 if (a_pKey) {
2121 typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
2122 if (iKeyVal == iSection->second.end()) {
2123 return false;
2126 // remove any copied strings and then the key
2127 typename TKeyVal::iterator iDelete;
2128 do {
2129 iDelete = iKeyVal++;
2131 DeleteString(iDelete->first.pItem);
2132 DeleteString(iDelete->second);
2133 iSection->second.erase(iDelete);
2135 while (iKeyVal != iSection->second.end()
2136 && !IsLess(a_pKey, iKeyVal->first.pItem));
2138 // done now if the section is not empty or we are not pruning away
2139 // the empty sections. Otherwise let it fall through into the section
2140 // deletion code
2141 if (!a_bRemoveEmpty || !iSection->second.empty()) {
2142 return true;
2145 else {
2146 // delete all copied strings from this section. The actual
2147 // entries will be removed when the section is removed.
2148 typename TKeyVal::iterator iKeyVal = iSection->second.begin();
2149 for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
2150 DeleteString(iKeyVal->first.pItem);
2151 DeleteString(iKeyVal->second);
2155 // delete the section itself
2156 DeleteString(iSection->first.pItem);
2157 m_data.erase(iSection);
2159 return true;
2162 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2163 void
2164 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString(
2165 const SI_CHAR * a_pString
2168 // strings may exist either inside the data block, or they will be
2169 // individually allocated and stored in m_strings. We only physically
2170 // delete those stored in m_strings.
2171 if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
2172 typename TNamesDepend::iterator i = m_strings.begin();
2173 for (;i != m_strings.end(); ++i) {
2174 if (a_pString == i->pItem) {
2175 delete[] const_cast<SI_CHAR*>(i->pItem);
2176 m_strings.erase(i);
2177 break;
2183 // ---------------------------------------------------------------------------
2184 // CONVERSION FUNCTIONS
2185 // ---------------------------------------------------------------------------
2187 // Defines the conversion classes for different libraries. Before including
2188 // SimpleIni.h, set the converter that you wish you use by defining one of the
2189 // following symbols.
2191 // SI_CONVERT_GENERIC Use the Unicode reference conversion library in
2192 // the accompanying files ConvertUTF.h/c
2193 // SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
2194 // ICU headers on include path and icuuc.lib
2195 // SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
2197 #if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
2198 # ifdef _WIN32
2199 # define SI_CONVERT_WIN32
2200 # else
2201 # define SI_CONVERT_GENERIC
2202 # endif
2203 #endif
2206 * Generic case-sensitive less than comparison. This class returns numerically
2207 * ordered ASCII case-sensitive text for all possible sizes and types of
2208 * SI_CHAR.
2210 template<class SI_CHAR>
2211 struct SI_GenericCase {
2212 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2213 long cmp;
2214 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2215 cmp = (long) *pLeft - (long) *pRight;
2216 if (cmp != 0) {
2217 return cmp < 0;
2220 return *pRight != 0;
2225 * Generic ASCII case-insensitive less than comparison. This class returns
2226 * numerically ordered ASCII case-insensitive text for all possible sizes
2227 * and types of SI_CHAR. It is not safe for MBCS text comparison where
2228 * ASCII A-Z characters are used in the encoding of multi-byte characters.
2230 template<class SI_CHAR>
2231 struct SI_GenericNoCase {
2232 inline SI_CHAR locase(SI_CHAR ch) const {
2233 return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
2235 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2236 long cmp;
2237 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2238 cmp = (long) locase(*pLeft) - (long) locase(*pRight);
2239 if (cmp != 0) {
2240 return cmp < 0;
2243 return *pRight != 0;
2248 * Null conversion class for MBCS/UTF-8 to char (or equivalent).
2250 template<class SI_CHAR>
2251 class SI_ConvertA {
2252 bool m_bStoreIsUtf8;
2253 protected:
2254 SI_ConvertA() { }
2255 public:
2256 SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
2258 /* copy and assignment */
2259 SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }
2260 SI_ConvertA & operator=(const SI_ConvertA & rhs) {
2261 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2262 return *this;
2265 /** Calculate the number of SI_CHAR required for converting the input
2266 * from the storage format. The storage format is always UTF-8 or MBCS.
2268 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2269 * @param a_uInputDataLen Length of storage format data in bytes. This
2270 * must be the actual length of the data, including
2271 * NULL byte if NULL terminated string is required.
2272 * @return Number of SI_CHAR required by the string when
2273 * converted. If there are embedded NULL bytes in the
2274 * input data, only the string up and not including
2275 * the NULL byte will be converted.
2276 * @return -1 cast to size_t on a conversion error.
2278 size_t SizeFromStore(
2279 const char * a_pInputData,
2280 size_t a_uInputDataLen)
2282 (void)a_pInputData;
2283 SI_ASSERT(a_uInputDataLen != (size_t) -1);
2285 // ASCII/MBCS/UTF-8 needs no conversion
2286 return a_uInputDataLen;
2289 /** Convert the input string from the storage format to SI_CHAR.
2290 * The storage format is always UTF-8 or MBCS.
2292 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2293 * @param a_uInputDataLen Length of storage format data in bytes. This
2294 * must be the actual length of the data, including
2295 * NULL byte if NULL terminated string is required.
2296 * @param a_pOutputData Pointer to the output buffer to received the
2297 * converted data.
2298 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2299 * @return true if all of the input data was successfully
2300 * converted.
2302 bool ConvertFromStore(
2303 const char * a_pInputData,
2304 size_t a_uInputDataLen,
2305 SI_CHAR * a_pOutputData,
2306 size_t a_uOutputDataSize)
2308 // ASCII/MBCS/UTF-8 needs no conversion
2309 if (a_uInputDataLen > a_uOutputDataSize) {
2310 return false;
2312 memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
2313 return true;
2316 /** Calculate the number of char required by the storage format of this
2317 * data. The storage format is always UTF-8 or MBCS.
2319 * @param a_pInputData NULL terminated string to calculate the number of
2320 * bytes required to be converted to storage format.
2321 * @return Number of bytes required by the string when
2322 * converted to storage format. This size always
2323 * includes space for the terminating NULL character.
2324 * @return -1 cast to size_t on a conversion error.
2326 size_t SizeToStore(
2327 const SI_CHAR * a_pInputData)
2329 // ASCII/MBCS/UTF-8 needs no conversion
2330 return strlen((const char *)a_pInputData) + 1;
2333 /** Convert the input string to the storage format of this data.
2334 * The storage format is always UTF-8 or MBCS.
2336 * @param a_pInputData NULL terminated source string to convert. All of
2337 * the data will be converted including the
2338 * terminating NULL character.
2339 * @param a_pOutputData Pointer to the buffer to receive the converted
2340 * string.
2341 * @param a_uOutputDataSize Size of the output buffer in char.
2342 * @return true if all of the input data, including the
2343 * terminating NULL character was successfully
2344 * converted.
2346 bool ConvertToStore(
2347 const SI_CHAR * a_pInputData,
2348 char * a_pOutputData,
2349 size_t a_uOutputDataSize)
2351 // calc input string length (SI_CHAR type and size independent)
2352 size_t uInputLen = strlen((const char *)a_pInputData) + 1;
2353 if (uInputLen > a_uOutputDataSize) {
2354 return false;
2357 // ascii/UTF-8 needs no conversion
2358 memcpy(a_pOutputData, a_pInputData, uInputLen);
2359 return true;
2364 // ---------------------------------------------------------------------------
2365 // SI_CONVERT_GENERIC
2366 // ---------------------------------------------------------------------------
2367 #ifdef SI_CONVERT_GENERIC
2369 #define SI_Case SI_GenericCase
2370 #define SI_NoCase SI_GenericNoCase
2372 #include <wchar.h>
2373 #include "ConvertUTF.h"
2376 * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference
2377 * library functions. This can be used on all platforms.
2379 template<class SI_CHAR>
2380 class SI_ConvertW {
2381 bool m_bStoreIsUtf8;
2382 protected:
2383 SI_ConvertW() { }
2384 public:
2385 SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
2387 /* copy and assignment */
2388 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
2389 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
2390 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2391 return *this;
2394 /** Calculate the number of SI_CHAR required for converting the input
2395 * from the storage format. The storage format is always UTF-8 or MBCS.
2397 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2398 * @param a_uInputDataLen Length of storage format data in bytes. This
2399 * must be the actual length of the data, including
2400 * NULL byte if NULL terminated string is required.
2401 * @return Number of SI_CHAR required by the string when
2402 * converted. If there are embedded NULL bytes in the
2403 * input data, only the string up and not including
2404 * the NULL byte will be converted.
2405 * @return -1 cast to size_t on a conversion error.
2407 size_t SizeFromStore(
2408 const char * a_pInputData,
2409 size_t a_uInputDataLen)
2411 SI_ASSERT(a_uInputDataLen != (size_t) -1);
2413 if (m_bStoreIsUtf8) {
2414 // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
2415 // so we just return the same number of characters required as for
2416 // the source text.
2417 return a_uInputDataLen;
2419 else {
2420 return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
2424 /** Convert the input string from the storage format to SI_CHAR.
2425 * The storage format is always UTF-8 or MBCS.
2427 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2428 * @param a_uInputDataLen Length of storage format data in bytes. This
2429 * must be the actual length of the data, including
2430 * NULL byte if NULL terminated string is required.
2431 * @param a_pOutputData Pointer to the output buffer to received the
2432 * converted data.
2433 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2434 * @return true if all of the input data was successfully
2435 * converted.
2437 bool ConvertFromStore(
2438 const char * a_pInputData,
2439 size_t a_uInputDataLen,
2440 SI_CHAR * a_pOutputData,
2441 size_t a_uOutputDataSize)
2443 if (m_bStoreIsUtf8) {
2444 // This uses the Unicode reference implementation to do the
2445 // conversion from UTF-8 to wchar_t. The required files are
2446 // ConvertUTF.h and ConvertUTF.c which should be included in
2447 // the distribution but are publically available from unicode.org
2448 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
2449 ConversionResult retval;
2450 const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
2451 if (sizeof(wchar_t) == sizeof(UTF32)) {
2452 UTF32 * pUtf32 = (UTF32 *) a_pOutputData;
2453 retval = ConvertUTF8toUTF32(
2454 &pUtf8, pUtf8 + a_uInputDataLen,
2455 &pUtf32, pUtf32 + a_uOutputDataSize,
2456 lenientConversion);
2458 else if (sizeof(wchar_t) == sizeof(UTF16)) {
2459 UTF16 * pUtf16 = (UTF16 *) a_pOutputData;
2460 retval = ConvertUTF8toUTF16(
2461 &pUtf8, pUtf8 + a_uInputDataLen,
2462 &pUtf16, pUtf16 + a_uOutputDataSize,
2463 lenientConversion);
2465 return retval == conversionOK;
2467 else {
2468 size_t retval = mbstowcs(a_pOutputData,
2469 a_pInputData, a_uOutputDataSize);
2470 return retval != (size_t)(-1);
2474 /** Calculate the number of char required by the storage format of this
2475 * data. The storage format is always UTF-8 or MBCS.
2477 * @param a_pInputData NULL terminated string to calculate the number of
2478 * bytes required to be converted to storage format.
2479 * @return Number of bytes required by the string when
2480 * converted to storage format. This size always
2481 * includes space for the terminating NULL character.
2482 * @return -1 cast to size_t on a conversion error.
2484 size_t SizeToStore(
2485 const SI_CHAR * a_pInputData)
2487 if (m_bStoreIsUtf8) {
2488 // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
2489 size_t uLen = 0;
2490 while (a_pInputData[uLen]) {
2491 ++uLen;
2493 return (6 * uLen) + 1;
2495 else {
2496 size_t uLen = wcstombs(NULL, a_pInputData, 0);
2497 if (uLen == (size_t)(-1)) {
2498 return uLen;
2500 return uLen + 1; // include NULL terminator
2504 /** Convert the input string to the storage format of this data.
2505 * The storage format is always UTF-8 or MBCS.
2507 * @param a_pInputData NULL terminated source string to convert. All of
2508 * the data will be converted including the
2509 * terminating NULL character.
2510 * @param a_pOutputData Pointer to the buffer to receive the converted
2511 * string.
2512 * @param a_uOutputDataSize Size of the output buffer in char.
2513 * @return true if all of the input data, including the
2514 * terminating NULL character was successfully
2515 * converted.
2517 bool ConvertToStore(
2518 const SI_CHAR * a_pInputData,
2519 char * a_pOutputData,
2520 size_t a_uOutputDataSize
2523 if (m_bStoreIsUtf8) {
2524 // calc input string length (SI_CHAR type and size independent)
2525 size_t uInputLen = 0;
2526 while (a_pInputData[uInputLen]) {
2527 ++uInputLen;
2529 ++uInputLen; // include the NULL char
2531 // This uses the Unicode reference implementation to do the
2532 // conversion from wchar_t to UTF-8. The required files are
2533 // ConvertUTF.h and ConvertUTF.c which should be included in
2534 // the distribution but are publically available from unicode.org
2535 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
2536 ConversionResult retval;
2537 UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
2538 if (sizeof(wchar_t) == sizeof(UTF32)) {
2539 const UTF32 * pUtf32 = (const UTF32 *) a_pInputData;
2540 retval = ConvertUTF32toUTF8(
2541 &pUtf32, pUtf32 + uInputLen + 1,
2542 &pUtf8, pUtf8 + a_uOutputDataSize,
2543 lenientConversion);
2545 else if (sizeof(wchar_t) == sizeof(UTF16)) {
2546 const UTF16 * pUtf16 = (const UTF16 *) a_pInputData;
2547 retval = ConvertUTF16toUTF8(
2548 &pUtf16, pUtf16 + uInputLen + 1,
2549 &pUtf8, pUtf8 + a_uOutputDataSize,
2550 lenientConversion);
2552 return retval == conversionOK;
2554 else {
2555 size_t retval = wcstombs(a_pOutputData,
2556 a_pInputData, a_uOutputDataSize);
2557 return retval != (size_t) -1;
2562 #endif // SI_CONVERT_GENERIC
2565 // ---------------------------------------------------------------------------
2566 // SI_CONVERT_ICU
2567 // ---------------------------------------------------------------------------
2568 #ifdef SI_CONVERT_ICU
2570 #define SI_Case SI_GenericCase
2571 #define SI_NoCase SI_GenericNoCase
2573 #include <unicode/ucnv.h>
2576 * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms.
2578 template<class SI_CHAR>
2579 class SI_ConvertW {
2580 const char * m_pEncoding;
2581 UConverter * m_pConverter;
2582 protected:
2583 SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
2584 public:
2585 SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
2586 m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
2589 /* copy and assignment */
2590 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
2591 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
2592 m_pEncoding = rhs.m_pEncoding;
2593 m_pConverter = NULL;
2594 return *this;
2596 ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); }
2598 /** Calculate the number of UChar required for converting the input
2599 * from the storage format. The storage format is always UTF-8 or MBCS.
2601 * @param a_pInputData Data in storage format to be converted to UChar.
2602 * @param a_uInputDataLen Length of storage format data in bytes. This
2603 * must be the actual length of the data, including
2604 * NULL byte if NULL terminated string is required.
2605 * @return Number of UChar required by the string when
2606 * converted. If there are embedded NULL bytes in the
2607 * input data, only the string up and not including
2608 * the NULL byte will be converted.
2609 * @return -1 cast to size_t on a conversion error.
2611 size_t SizeFromStore(
2612 const char * a_pInputData,
2613 size_t a_uInputDataLen)
2615 SI_ASSERT(a_uInputDataLen != (size_t) -1);
2617 UErrorCode nError;
2619 if (!m_pConverter) {
2620 nError = U_ZERO_ERROR;
2621 m_pConverter = ucnv_open(m_pEncoding, &nError);
2622 if (U_FAILURE(nError)) {
2623 return (size_t) -1;
2627 nError = U_ZERO_ERROR;
2628 ucnv_resetToUnicode(m_pConverter);
2629 int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,
2630 a_pInputData, (int32_t) a_uInputDataLen, &nError);
2631 if (nError != U_BUFFER_OVERFLOW_ERROR) {
2632 return (size_t) -1;
2635 return (size_t) nLen;
2638 /** Convert the input string from the storage format to UChar.
2639 * The storage format is always UTF-8 or MBCS.
2641 * @param a_pInputData Data in storage format to be converted to UChar.
2642 * @param a_uInputDataLen Length of storage format data in bytes. This
2643 * must be the actual length of the data, including
2644 * NULL byte if NULL terminated string is required.
2645 * @param a_pOutputData Pointer to the output buffer to received the
2646 * converted data.
2647 * @param a_uOutputDataSize Size of the output buffer in UChar.
2648 * @return true if all of the input data was successfully
2649 * converted.
2651 bool ConvertFromStore(
2652 const char * a_pInputData,
2653 size_t a_uInputDataLen,
2654 UChar * a_pOutputData,
2655 size_t a_uOutputDataSize)
2657 UErrorCode nError;
2659 if (!m_pConverter) {
2660 nError = U_ZERO_ERROR;
2661 m_pConverter = ucnv_open(m_pEncoding, &nError);
2662 if (U_FAILURE(nError)) {
2663 return false;
2667 nError = U_ZERO_ERROR;
2668 ucnv_resetToUnicode(m_pConverter);
2669 ucnv_toUChars(m_pConverter,
2670 a_pOutputData, (int32_t) a_uOutputDataSize,
2671 a_pInputData, (int32_t) a_uInputDataLen, &nError);
2672 if (U_FAILURE(nError)) {
2673 return false;
2676 return true;
2679 /** Calculate the number of char required by the storage format of this
2680 * data. The storage format is always UTF-8 or MBCS.
2682 * @param a_pInputData NULL terminated string to calculate the number of
2683 * bytes required to be converted to storage format.
2684 * @return Number of bytes required by the string when
2685 * converted to storage format. This size always
2686 * includes space for the terminating NULL character.
2687 * @return -1 cast to size_t on a conversion error.
2689 size_t SizeToStore(
2690 const UChar * a_pInputData)
2692 UErrorCode nError;
2694 if (!m_pConverter) {
2695 nError = U_ZERO_ERROR;
2696 m_pConverter = ucnv_open(m_pEncoding, &nError);
2697 if (U_FAILURE(nError)) {
2698 return (size_t) -1;
2702 nError = U_ZERO_ERROR;
2703 ucnv_resetFromUnicode(m_pConverter);
2704 int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,
2705 a_pInputData, -1, &nError);
2706 if (nError != U_BUFFER_OVERFLOW_ERROR) {
2707 return (size_t) -1;
2710 return (size_t) nLen + 1;
2713 /** Convert the input string to the storage format of this data.
2714 * The storage format is always UTF-8 or MBCS.
2716 * @param a_pInputData NULL terminated source string to convert. All of
2717 * the data will be converted including the
2718 * terminating NULL character.
2719 * @param a_pOutputData Pointer to the buffer to receive the converted
2720 * string.
2721 * @param a_pOutputDataSize Size of the output buffer in char.
2722 * @return true if all of the input data, including the
2723 * terminating NULL character was successfully
2724 * converted.
2726 bool ConvertToStore(
2727 const UChar * a_pInputData,
2728 char * a_pOutputData,
2729 size_t a_uOutputDataSize)
2731 UErrorCode nError;
2733 if (!m_pConverter) {
2734 nError = U_ZERO_ERROR;
2735 m_pConverter = ucnv_open(m_pEncoding, &nError);
2736 if (U_FAILURE(nError)) {
2737 return false;
2741 nError = U_ZERO_ERROR;
2742 ucnv_resetFromUnicode(m_pConverter);
2743 ucnv_fromUChars(m_pConverter,
2744 a_pOutputData, (int32_t) a_uOutputDataSize,
2745 a_pInputData, -1, &nError);
2746 if (U_FAILURE(nError)) {
2747 return false;
2750 return true;
2754 #endif // SI_CONVERT_ICU
2757 // ---------------------------------------------------------------------------
2758 // SI_CONVERT_WIN32
2759 // ---------------------------------------------------------------------------
2760 #ifdef SI_CONVERT_WIN32
2762 #define SI_Case SI_GenericCase
2764 // Windows CE doesn't have errno or MBCS libraries
2765 #ifdef _WIN32_WCE
2766 # ifndef SI_NO_MBCS
2767 # define SI_NO_MBCS
2768 # endif
2769 #endif
2771 #include <windows.h>
2772 #ifdef SI_NO_MBCS
2773 # define SI_NoCase SI_GenericNoCase
2774 #else // !SI_NO_MBCS
2776 * Case-insensitive comparison class using Win32 MBCS functions. This class
2777 * returns a case-insensitive semi-collation order for MBCS text. It may not
2778 * be safe for UTF-8 text returned in char format as we don't know what
2779 * characters will be folded by the function! Therefore, if you are using
2780 * SI_CHAR == char and SetUnicode(true), then you need to use the generic
2781 * SI_NoCase class instead.
2783 #include <mbstring.h>
2784 template<class SI_CHAR>
2785 struct SI_NoCase {
2786 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2787 if (sizeof(SI_CHAR) == sizeof(char)) {
2788 return _mbsicmp((const unsigned char *)pLeft,
2789 (const unsigned char *)pRight) < 0;
2791 if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
2792 return _wcsicmp((const wchar_t *)pLeft,
2793 (const wchar_t *)pRight) < 0;
2795 return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight);
2798 #endif // SI_NO_MBCS
2801 * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses
2802 * only the Win32 functions and doesn't require the external Unicode UTF-8
2803 * conversion library. It will not work on Windows 95 without using Microsoft
2804 * Layer for Unicode in your application.
2806 template<class SI_CHAR>
2807 class SI_ConvertW {
2808 UINT m_uCodePage;
2809 protected:
2810 SI_ConvertW() { }
2811 public:
2812 SI_ConvertW(bool a_bStoreIsUtf8) {
2813 m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP;
2816 /* copy and assignment */
2817 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
2818 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
2819 m_uCodePage = rhs.m_uCodePage;
2820 return *this;
2823 /** Calculate the number of SI_CHAR required for converting the input
2824 * from the storage format. The storage format is always UTF-8 or MBCS.
2826 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2827 * @param a_uInputDataLen Length of storage format data in bytes. This
2828 * must be the actual length of the data, including
2829 * NULL byte if NULL terminated string is required.
2830 * @return Number of SI_CHAR required by the string when
2831 * converted. If there are embedded NULL bytes in the
2832 * input data, only the string up and not including
2833 * the NULL byte will be converted.
2834 * @return -1 cast to size_t on a conversion error.
2836 size_t SizeFromStore(
2837 const char * a_pInputData,
2838 size_t a_uInputDataLen)
2840 SI_ASSERT(a_uInputDataLen != (size_t) -1);
2842 int retval = MultiByteToWideChar(
2843 m_uCodePage, 0,
2844 a_pInputData, (int) a_uInputDataLen,
2845 0, 0);
2846 return (size_t)(retval > 0 ? retval : -1);
2849 /** Convert the input string from the storage format to SI_CHAR.
2850 * The storage format is always UTF-8 or MBCS.
2852 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2853 * @param a_uInputDataLen Length of storage format data in bytes. This
2854 * must be the actual length of the data, including
2855 * NULL byte if NULL terminated string is required.
2856 * @param a_pOutputData Pointer to the output buffer to received the
2857 * converted data.
2858 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2859 * @return true if all of the input data was successfully
2860 * converted.
2862 bool ConvertFromStore(
2863 const char * a_pInputData,
2864 size_t a_uInputDataLen,
2865 SI_CHAR * a_pOutputData,
2866 size_t a_uOutputDataSize)
2868 int nSize = MultiByteToWideChar(
2869 m_uCodePage, 0,
2870 a_pInputData, (int) a_uInputDataLen,
2871 (wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
2872 return (nSize > 0);
2875 /** Calculate the number of char required by the storage format of this
2876 * data. The storage format is always UTF-8.
2878 * @param a_pInputData NULL terminated string to calculate the number of
2879 * bytes required to be converted to storage format.
2880 * @return Number of bytes required by the string when
2881 * converted to storage format. This size always
2882 * includes space for the terminating NULL character.
2883 * @return -1 cast to size_t on a conversion error.
2885 size_t SizeToStore(
2886 const SI_CHAR * a_pInputData)
2888 int retval = WideCharToMultiByte(
2889 m_uCodePage, 0,
2890 (const wchar_t *) a_pInputData, -1,
2891 0, 0, 0, 0);
2892 return (size_t) (retval > 0 ? retval : -1);
2895 /** Convert the input string to the storage format of this data.
2896 * The storage format is always UTF-8 or MBCS.
2898 * @param a_pInputData NULL terminated source string to convert. All of
2899 * the data will be converted including the
2900 * terminating NULL character.
2901 * @param a_pOutputData Pointer to the buffer to receive the converted
2902 * string.
2903 * @param a_pOutputDataSize Size of the output buffer in char.
2904 * @return true if all of the input data, including the
2905 * terminating NULL character was successfully
2906 * converted.
2908 bool ConvertToStore(
2909 const SI_CHAR * a_pInputData,
2910 char * a_pOutputData,
2911 size_t a_uOutputDataSize)
2913 int retval = WideCharToMultiByte(
2914 m_uCodePage, 0,
2915 (const wchar_t *) a_pInputData, -1,
2916 a_pOutputData, (int) a_uOutputDataSize, 0, 0);
2917 return retval > 0;
2921 #endif // SI_CONVERT_WIN32
2924 // ---------------------------------------------------------------------------
2925 // TYPE DEFINITIONS
2926 // ---------------------------------------------------------------------------
2928 typedef CSimpleIniTempl<char,
2929 SI_NoCase<char>,SI_ConvertA<char> > CSimpleIniA;
2930 typedef CSimpleIniTempl<char,
2931 SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA;
2933 #if defined(SI_CONVERT_ICU)
2934 typedef CSimpleIniTempl<UChar,
2935 SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;
2936 typedef CSimpleIniTempl<UChar,
2937 SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;
2938 #else
2939 typedef CSimpleIniTempl<wchar_t,
2940 SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;
2941 typedef CSimpleIniTempl<wchar_t,
2942 SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;
2943 #endif
2945 #ifdef _UNICODE
2946 # define CSimpleIni CSimpleIniW
2947 # define CSimpleIniCase CSimpleIniCaseW
2948 # define SI_NEWLINE SI_NEWLINE_W
2949 #else // !_UNICODE
2950 # define CSimpleIni CSimpleIniA
2951 # define CSimpleIniCase CSimpleIniCaseA
2952 # define SI_NEWLINE SI_NEWLINE_A
2953 #endif // _UNICODE
2955 #ifdef _MSC_VER
2956 # pragma warning (pop)
2957 #endif
2959 #endif // INCLUDED_SimpleIni_h