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