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 95/98/ME/NT/2K/XP/2003, Windows CE, Linux, Unix)
24 - loading and saving of INI-style configuration files
25 - configuration files can have any newline format on all platforms
26 - liberal acceptance of file format
27 - key/values with no section
28 - removal of whitespace around sections, keys and values
29 - support for multi-line values (values with embedded newline characters)
30 - optional support for multiple keys with the same name
31 - optional case-insensitive sections and keys (for ASCII characters only)
32 - saves files with sections and keys in the same order as they were loaded
33 - preserves comments on the file, section and keys where possible.
34 - supports both char or wchar_t programming interfaces
35 - supports both MBCS (system locale) and UTF-8 file encodings
36 - system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file
37 - support for non-ASCII characters in section, keys, values and comments
38 - support for non-standard character types or file encodings
39 via user-written converter classes
40 - support for adding/modifying values programmatically
41 - compiles cleanly in the following compilers:
42 - Windows/VC6 (warning level 3)
43 - Windows/VC.NET 2003 (warning level 4)
44 - Windows/VC 2005 (warning level 4)
45 - Linux/gcc (-Wall)
46
47
48 @section usage USAGE SUMMARY
49
50 -# Define the appropriate symbol for the converter you wish to use and
51 include the SimpleIni.h header file. If no specific converter is defined
52 then the default converter is used. The default conversion mode uses
53 SI_CONVERT_WIN32 on Windows and SI_CONVERT_GENERIC on all other
54 platforms. If you are using ICU then SI_CONVERT_ICU is supported on all
55 platforms.
56 -# Declare an instance the appropriate class. Note that the following
57 definitions are just shortcuts for commonly used types. Other types
58 (PRUnichar, unsigned short, unsigned char) are also possible.
59 <table>
60 <tr><th>Interface <th>Case-sensitive <th>Load UTF-8 <th>Load MBCS <th>Typedef
61 <tr><th>SI_CONVERT_GENERIC
62 <tr><td>char <td>No <td>Yes <td>Yes #1 <td>CSimpleIniA
63 <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
64 <tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW
65 <tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
66 <tr><th>SI_CONVERT_WIN32
67 <tr><td>char <td>No <td>No #2 <td>Yes <td>CSimpleIniA
68 <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
69 <tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW
70 <tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
71 <tr><th>SI_CONVERT_ICU
72 <tr><td>char <td>No <td>Yes <td>Yes <td>CSimpleIniA
73 <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
74 <tr><td>UChar <td>No <td>Yes <td>Yes <td>CSimpleIniW
75 <tr><td>UChar <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
76 </table>
77 #1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.<br>
78 #2 Only affects Windows. On Windows this uses MBCS functions and
79 so may fold case incorrectly leading to uncertain results.
80 -# Call LoadData() or LoadFile() to load and parse the INI configuration file
81 -# Access and modify the data of the file using the following functions
82 <table>
83 <tr><td>GetAllSections <td>Return all section names
84 <tr><td>GetAllKeys <td>Return all key names within a section
85 <tr><td>GetAllValues <td>Return all values within a section & key
86 <tr><td>GetSection <td>Return all key names and values in a section
87 <tr><td>GetSectionSize <td>Return the number of keys in a section
88 <tr><td>GetValue <td>Return a value for a section & key
89 <tr><td>SetValue <td>Add or update a value for a section & key
90 <tr><td>Delete <td>Remove a section, or a key from a section
91 </table>
92 -# Call Save() or SaveFile() to save the INI configuration data
93
94 @section iostreams IO STREAMS
95
96 SimpleIni supports reading from and writing to STL IO streams. Enable this
97 by defining SI_SUPPORT_IOSTREAMS before including the SimpleIni.h header
98 file. Ensure that if the streams are backed by a file (e.g. ifstream or
99 ofstream) then the flag ios_base::binary has been used when the file was
100 opened.
101
102 @section multiline MULTI-LINE VALUES
103
104 Values that span multiple lines are created using the following format.
105
106 <pre>
107 key = <<<ENDTAG
108 .... multiline value ....
109 ENDTAG
110 </pre>
111
112 Note the following:
113 - The text used for ENDTAG can be anything and is used to find
114 where the multi-line text ends.
115 - The newline after ENDTAG in the start tag, and the newline
116 before ENDTAG in the end tag is not included in the data value.
117 - The ending tag must be on it's own line with no whitespace before
118 or after it.
119 - The multi-line value is modified at load so that each line in the value
120 is delimited by a single '\\n' character on all platforms. At save time
121 it will be converted into the newline format used by the current
122 platform.
123
124 @section comments COMMENTS
125
126 Comments are preserved in the file within the following restrictions:
127 - Every file may have a single "file comment". It must start with the
128 first character in the file, and will end with the first non-comment
129 line in the file.
130 - Every section may have a single "section comment". It will start
131 with the first comment line following the file comment, or the last
132 data entry. It ends at the beginning of the section.
133 - Every key may have a single "key comment". This comment will start
134 with the first comment line following the section start, or the file
135 comment if there is no section name.
136 - Comments are set at the time that the file, section or key is first
137 created. The only way to modify a comment on a section or a key is to
138 delete that entry and recreate it with the new comment. There is no
139 way to change the file comment.
140
141 @section save SAVE ORDER
142
143 The sections and keys are written out in the same order as they were
144 read in from the file. Sections and keys added to the data after the
145 file has been loaded will be added to the end of the file when it is
146 written. There is no way to specify the location of a section or key
147 other than in first-created, first-saved order.
148
149 @section notes NOTES
150
151 - To load UTF-8 data on Windows 95, you need to use Microsoft Layer for
152 Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU.
153 - When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked.
154 - When using SI_CONVERT_ICU, ICU header files must be on the include
155 path and icuuc.lib must be linked in.
156 - To load a UTF-8 file on Windows AND expose it with SI_CHAR == char,
157 you should use SI_CONVERT_GENERIC.
158 - The collation (sorting) order used for sections and keys returned from
159 iterators is NOT DEFINED. If collation order of the text is important
160 then it should be done yourself by either supplying a replacement
161 SI_STRLESS class, or by sorting the strings external to this library.
162 - Usage of the <mbstring.h> header on Windows can be disabled by defining
163 SI_NO_MBCS. This is defined automatically on Windows CE platforms.
164 - 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 : std::binary_function<Entry, Entry, bool> {
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 : std::binary_function<Entry, Entry, bool> {
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 SI_CONVERTER converter(m_bStoreIsUtf8);
1418
1419 // consume the UTF-8 BOM if it exists
1420 if (m_bStoreIsUtf8 && a_uDataLen >= 3) {
1421 if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
1422 a_pData += 3;
1423 a_uDataLen -= 3;
1424 }
1425 }
1426
1427 if (a_uDataLen == 0) {
1428 return SI_OK;
1429 }
1430
1431 // determine the length of the converted data
1432 size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
1433 if (uLen == (size_t)(-1)) {
1434 return SI_FAIL;
1435 }
1436
1437 // allocate memory for the data, ensure that there is a NULL
1438 // terminator wherever the converted data ends
1439 SI_CHAR * pData = new(std::nothrow) SI_CHAR[uLen+1];
1440 if (!pData) {
1441 return SI_NOMEM;
1442 }
1443 memset(pData, 0, sizeof(SI_CHAR)*(uLen+1));
1444
1445 // convert the data
1446 if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
1447 delete[] pData;
1448 return SI_FAIL;
1449 }
1450
1451 // parse it
1452 const static SI_CHAR empty = 0;
1453 SI_CHAR * pWork = pData;
1454 const SI_CHAR * pSection = &empty;
1455 const SI_CHAR * pItem = NULL;
1456 const SI_CHAR * pVal = NULL;
1457 const SI_CHAR * pComment = NULL;
1458
1459 // We copy the strings if we are loading data into this class when we
1460 // already have stored some.
1461 bool bCopyStrings = (m_pData != NULL);
1462
1463 // find a file comment if it exists, this is a comment that starts at the
1464 // beginning of the file and continues until the first blank line.
1465 SI_Error rc = FindFileComment(pWork, bCopyStrings);
1466 if (rc < 0) return rc;
1467
1468 // add every entry in the file to the data table
1469 while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
1470 rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings);
1471 if (rc < 0) return rc;
1472 }
1473
1474 // store these strings if we didn't copy them
1475 if (bCopyStrings) {
1476 delete[] pData;
1477 }
1478 else {
1479 m_pData = pData;
1480 m_uDataLen = uLen+1;
1481 }
1482
1483 return SI_OK;
1484}
1485
1486#ifdef SI_SUPPORT_IOSTREAMS
1487template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1488SI_Error
1489CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(
1490 std::istream & a_istream
1491 )
1492{
1493 std::string strData;
1494 char szBuf[512];
1495 do {
1496 a_istream.get(szBuf, sizeof(szBuf), '\0');
1497 strData.append(szBuf);
1498 }
1499 while (a_istream.good());
1500 return LoadData(strData);
1501}
1502#endif // SI_SUPPORT_IOSTREAMS
1503
1504template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1505SI_Error
1506CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindFileComment(
1507 SI_CHAR *& a_pData,
1508 bool a_bCopyStrings
1509 )
1510{
1511 // there can only be a single file comment
1512 if (m_pFileComment) {
1513 return SI_OK;
1514 }
1515
1516 // Load the file comment as multi-line text, this will modify all of
1517 // the newline characters to be single \n chars
1518 if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
1519 return SI_OK;
1520 }
1521
1522 // copy the string if necessary
1523 if (a_bCopyStrings) {
1524 SI_Error rc = CopyString(m_pFileComment);
1525 if (rc < 0) return rc;
1526 }
1527
1528 return SI_OK;
1529}
1530
1531template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1532bool
1533CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
1534 SI_CHAR *& a_pData,
1535 const SI_CHAR *& a_pSection,
1536 const SI_CHAR *& a_pKey,
1537 const SI_CHAR *& a_pVal,
1538 const SI_CHAR *& a_pComment
1539 ) const
1540{
1541 a_pComment = NULL;
1542
1543 SI_CHAR * pTrail = NULL;
1544 while (*a_pData) {
1545 // skip spaces and empty lines
1546 while (*a_pData && IsSpace(*a_pData)) {
1547 ++a_pData;
1548 }
1549 if (!*a_pData) {
1550 break;
1551 }
1552
1553 // skip processing of comment lines but keep a pointer to
1554 // the start of the comment.
1555 if (IsComment(*a_pData)) {
1556 LoadMultiLineText(a_pData, a_pComment, NULL, true);
1557 continue;
1558 }
1559
1560 // process section names
1561 if (*a_pData == '[') {
1562 // skip leading spaces
1563 ++a_pData;
1564 while (*a_pData && IsSpace(*a_pData)) {
1565 ++a_pData;
1566 }
1567
1568 // find the end of the section name (it may contain spaces)
1569 // and convert it to lowercase as necessary
1570 a_pSection = a_pData;
1571 while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
1572 ++a_pData;
1573 }
1574
1575 // if it's an invalid line, just skip it
1576 if (*a_pData != ']') {
1577 continue;
1578 }
1579
1580 // remove trailing spaces from the section
1581 pTrail = a_pData - 1;
1582 while (pTrail >= a_pSection && IsSpace(*pTrail)) {
1583 --pTrail;
1584 }
1585 ++pTrail;
1586 *pTrail = 0;
1587
1588 // skip to the end of the line
1589 ++a_pData; // safe as checked that it == ']' above
1590 while (*a_pData && !IsNewLineChar(*a_pData)) {
1591 ++a_pData;
1592 }
1593
1594 a_pKey = NULL;
1595 a_pVal = NULL;
1596 return true;
1597 }
1598
1599 // find the end of the key name (it may contain spaces)
1600 // and convert it to lowercase as necessary
1601 a_pKey = a_pData;
1602 while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
1603 ++a_pData;
1604 }
1605
1606 // if it's an invalid line, just skip it
1607 if (*a_pData != '=') {
1608 continue;
1609 }
1610
1611 // empty keys are invalid
1612 if (a_pKey == a_pData) {
1613 while (*a_pData && !IsNewLineChar(*a_pData)) {
1614 ++a_pData;
1615 }
1616 continue;
1617 }
1618
1619 // remove trailing spaces from the key
1620 pTrail = a_pData - 1;
1621 while (pTrail >= a_pKey && IsSpace(*pTrail)) {
1622 --pTrail;
1623 }
1624 ++pTrail;
1625 *pTrail = 0;
1626
1627 // skip leading whitespace on the value
1628 ++a_pData; // safe as checked that it == '=' above
1629 while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
1630 ++a_pData;
1631 }
1632
1633 // find the end of the value which is the end of this line
1634 a_pVal = a_pData;
1635 while (*a_pData && !IsNewLineChar(*a_pData)) {
1636 ++a_pData;
1637 }
1638
1639 // remove trailing spaces from the value
1640 pTrail = a_pData - 1;
1641 if (*a_pData) { // prepare for the next round
1642 SkipNewLine(a_pData);
1643 }
1644 while (pTrail >= a_pVal && IsSpace(*pTrail)) {
1645 --pTrail;
1646 }
1647 ++pTrail;
1648 *pTrail = 0;
1649
1650 // check for multi-line entries
1651 if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
1652 // skip the "<<<" to get the tag that will end the multiline
1653 const SI_CHAR * pTagName = a_pVal + 3;
1654 return LoadMultiLineText(a_pData, a_pVal, pTagName);
1655 }
1656
1657 // return the standard entry
1658 return true;
1659 }
1660
1661 return false;
1662}
1663
1664template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1665bool
1666CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineTag(
1667 const SI_CHAR * a_pVal
1668 ) const
1669{
1670 // check for the "<<<" prefix for a multi-line entry
1671 if (*a_pVal++ != '<') return false;
1672 if (*a_pVal++ != '<') return false;
1673 if (*a_pVal++ != '<') return false;
1674 return true;
1675}
1676
1677template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1678bool
1679CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData(
1680 const SI_CHAR * a_pData
1681 ) const
1682{
1683 // data is multi-line if it has any of the following features:
1684 // * whitespace prefix
1685 // * embedded newlines
1686 // * whitespace suffix
1687
1688 // empty string
1689 if (!*a_pData) {
1690 return false;
1691 }
1692
1693 // check for prefix
1694 if (IsSpace(*a_pData)) {
1695 return true;
1696 }
1697
1698 // embedded newlines
1699 while (*a_pData) {
1700 if (IsNewLineChar(*a_pData)) {
1701 return true;
1702 }
1703 ++a_pData;
1704 }
1705
1706 // check for suffix
1707 if (IsSpace(*--a_pData)) {
1708 return true;
1709 }
1710
1711 return false;
1712}
1713
1714template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1715bool
1716CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar(
1717 SI_CHAR a_c
1718 ) const
1719{
1720 return (a_c == '\n' || a_c == '\r');
1721}
1722
1723template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1724bool
1725CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
1726 SI_CHAR *& a_pData,
1727 const SI_CHAR *& a_pVal,
1728 const SI_CHAR * a_pTagName,
1729 bool a_bAllowBlankLinesInComment
1730 ) const
1731{
1732 // we modify this data to strip all newlines down to a single '\n'
1733 // character. This means that on Windows we need to strip out some
1734 // characters which will make the data shorter.
1735 // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
1736 // LINE1-LINE1\nLINE2-LINE2\0
1737 // The pDataLine entry is the pointer to the location in memory that
1738 // the current line needs to start to run following the existing one.
1739 // This may be the same as pCurrLine in which case no move is needed.
1740 SI_CHAR * pDataLine = a_pData;
1741 SI_CHAR * pCurrLine;
1742
1743 // value starts at the current line
1744 a_pVal = a_pData;
1745
1746 // find the end tag. This tag must start in column 1 and be
1747 // followed by a newline. No whitespace removal is done while
1748 // searching for this tag.
1749 SI_CHAR cEndOfLineChar = *a_pData;
1750 for(;;) {
1751 // if we are loading comments then we need a comment character as
1752 // the first character on every line
1753 if (!a_pTagName && !IsComment(*a_pData)) {
1754 // if we aren't allowing blank lines then we're done
1755 if (!a_bAllowBlankLinesInComment) {
1756 break;
1757 }
1758
1759 // if we are allowing blank lines then we only include them
1760 // in this comment if another comment follows, so read ahead
1761 // to find out.
1762 SI_CHAR * pCurr = a_pData;
1763 int nNewLines = 0;
1764 while (IsSpace(*pCurr)) {
1765 if (IsNewLineChar(*pCurr)) {
1766 ++nNewLines;
1767 SkipNewLine(pCurr);
1768 }
1769 else {
1770 ++pCurr;
1771 }
1772 }
1773
1774 // we have a comment, add the blank lines to the output
1775 // and continue processing from here
1776 if (IsComment(*pCurr)) {
1777 for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n';
1778 a_pData = pCurr;
1779 continue;
1780 }
1781
1782 // the comment ends here
1783 break;
1784 }
1785
1786 // find the end of this line
1787 pCurrLine = a_pData;
1788 while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
1789
1790 // move this line down to the location that it should be if necessary
1791 if (pDataLine < pCurrLine) {
1792 size_t nLen = (size_t) (a_pData - pCurrLine);
1793 memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
1794 pDataLine[nLen] = '\0';
1795 }
1796
1797 // end the line with a NULL
1798 cEndOfLineChar = *a_pData;
1799 *a_pData = 0;
1800
1801 // if are looking for a tag then do the check now. This is done before
1802 // checking for end of the data, so that if we have the tag at the end
1803 // of the data then the tag is removed correctly.
1804 if (a_pTagName &&
1805 (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)))
1806 {
1807 break;
1808 }
1809
1810 // if we are at the end of the data then we just automatically end
1811 // this entry and return the current data.
1812 if (!cEndOfLineChar) {
1813 return true;
1814 }
1815
1816 // otherwise we need to process this newline to ensure that it consists
1817 // of just a single \n character.
1818 pDataLine += (a_pData - pCurrLine);
1819 *a_pData = cEndOfLineChar;
1820 SkipNewLine(a_pData);
1821 *pDataLine++ = '\n';
1822 }
1823
1824 // if we didn't find a comment at all then return false
1825 if (a_pVal == a_pData) {
1826 a_pVal = NULL;
1827 return false;
1828 }
1829
1830 // the data (which ends at the end of the last line) needs to be
1831 // null-terminated BEFORE before the newline character(s). If the
1832 // user wants a new line in the multi-line data then they need to
1833 // add an empty line before the tag.
1834 *--pDataLine = '\0';
1835
1836 // if looking for a tag and if we aren't at the end of the data,
1837 // then move a_pData to the start of the next line.
1838 if (a_pTagName && cEndOfLineChar) {
1839 SI_ASSERT(IsNewLineChar(cEndOfLineChar));
1840 *a_pData = cEndOfLineChar;
1841 SkipNewLine(a_pData);
1842 }
1843
1844 return true;
1845}
1846
1847template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1848SI_Error
1849CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString(
1850 const SI_CHAR *& a_pString
1851 )
1852{
1853 size_t uLen = 0;
1854 if (sizeof(SI_CHAR) == sizeof(char)) {
1855 uLen = strlen((const char *)a_pString);
1856 }
1857 else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
1858 uLen = wcslen((const wchar_t *)a_pString);
1859 }
1860 else {
1861 for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
1862 }
1863 ++uLen; // NULL character
1864 SI_CHAR * pCopy = new(std::nothrow) SI_CHAR[uLen];
1865 if (!pCopy) {
1866 return SI_NOMEM;
1867 }
1868 memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
1869 m_strings.push_back(pCopy);
1870 a_pString = pCopy;
1871 return SI_OK;
1872}
1873
1874template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1875SI_Error
1876CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
1877 const SI_CHAR * a_pSection,
1878 const SI_CHAR * a_pKey,
1879 const SI_CHAR * a_pValue,
1880 const SI_CHAR * a_pComment,
1881 bool a_bForceReplace,
1882 bool a_bCopyStrings
1883 )
1884{
1885 SI_Error rc;
1886 bool bInserted = false;
1887
1888 SI_ASSERT(!a_pComment || IsComment(*a_pComment));
1889
1890 // if we are copying strings then make a copy of the comment now
1891 // because we will need it when we add the entry.
1892 if (a_bCopyStrings && a_pComment) {
1893 rc = CopyString(a_pComment);
1894 if (rc < 0) return rc;
1895 }
1896
1897 // create the section entry if necessary
1898 typename TSection::iterator iSection = m_data.find(a_pSection);
1899 if (iSection == m_data.end()) {
1900 // if the section doesn't exist then we need a copy as the
1901 // string needs to last beyond the end of this function
1902 if (a_bCopyStrings) {
1903 rc = CopyString(a_pSection);
1904 if (rc < 0) return rc;
1905 }
1906
1907 // only set the comment if this is a section only entry
1908 Entry oSection(a_pSection, ++m_nOrder);
1909 if (a_pComment && (!a_pKey || !a_pValue)) {
1910 oSection.pComment = a_pComment;
1911 }
1912
1913 typename TSection::value_type oEntry(oSection, TKeyVal());
1914 typedef typename TSection::iterator SectionIterator;
1915 std::pair<SectionIterator,bool> i = m_data.insert(oEntry);
1916 iSection = i.first;
1917 bInserted = true;
1918 }
1919 if (!a_pKey || !a_pValue) {
1920 // section only entries are specified with pItem and pVal as NULL
1921 return bInserted ? SI_INSERTED : SI_UPDATED;
1922 }
1923
1924 // check for existence of the key
1925 TKeyVal & keyval = iSection->second;
1926 typename TKeyVal::iterator iKey = keyval.find(a_pKey);
1927
1928 // remove all existing entries but save the load order and
1929 // comment of the first entry
1930 int nLoadOrder = ++m_nOrder;
1931 if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) {
1932 const SI_CHAR * pComment = NULL;
1933 while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) {
1934 if (iKey->first.nOrder < nLoadOrder) {
1935 nLoadOrder = iKey->first.nOrder;
1936 pComment = iKey->first.pComment;
1937 }
1938 ++iKey;
1939 }
1940 if (pComment) {
1941 DeleteString(a_pComment);
1942 a_pComment = pComment;
1943 CopyString(a_pComment);
1944 }
1945 Delete(a_pSection, a_pKey);
1946 iKey = keyval.end();
1947 }
1948
1949 // make string copies if necessary
1950 bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
1951 if (a_bCopyStrings) {
1952 if (bForceCreateNewKey || iKey == keyval.end()) {
1953 // if the key doesn't exist then we need a copy as the
1954 // string needs to last beyond the end of this function
1955 // because we will be inserting the key next
1956 rc = CopyString(a_pKey);
1957 if (rc < 0) return rc;
1958 }
1959
1960 // we always need a copy of the value
1961 rc = CopyString(a_pValue);
1962 if (rc < 0) return rc;
1963 }
1964
1965 // create the key entry
1966 if (iKey == keyval.end() || bForceCreateNewKey) {
1967 Entry oKey(a_pKey, nLoadOrder);
1968 if (a_pComment) {
1969 oKey.pComment = a_pComment;
1970 }
1971 typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL));
1972 iKey = keyval.insert(oEntry);
1973 bInserted = true;
1974 }
1975 iKey->second = a_pValue;
1976 return bInserted ? SI_INSERTED : SI_UPDATED;
1977}
1978
1979template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1980const SI_CHAR *
1981CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetValue(
1982 const SI_CHAR * a_pSection,
1983 const SI_CHAR * a_pKey,
1984 const SI_CHAR * a_pDefault,
1985 bool * a_pHasMultiple
1986 ) const
1987{
1988 if (a_pHasMultiple) {
1989 *a_pHasMultiple = false;
1990 }
1991 if (!a_pSection || !a_pKey) {
1992 return a_pDefault;
1993 }
1994 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1995 if (iSection == m_data.end()) {
1996 return a_pDefault;
1997 }
1998 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
1999 if (iKeyVal == iSection->second.end()) {
2000 return a_pDefault;
2001 }
2002
2003 // check for multiple entries with the same key
2004 if (m_bAllowMultiKey && a_pHasMultiple) {
2005 typename TKeyVal::const_iterator iTemp = iKeyVal;
2006 if (++iTemp != iSection->second.end()) {
2007 if (!IsLess(a_pKey, iTemp->first.pItem)) {
2008 *a_pHasMultiple = true;
2009 }
2010 }
2011 }
2012
2013 return iKeyVal->second;
2014}
2015
2016template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2017long
2018CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetLongValue(
2019 const SI_CHAR * a_pSection,
2020 const SI_CHAR * a_pKey,
2021 long a_nDefault,
2022 bool * a_pHasMultiple
2023 ) const
2024{
2025 // return the default if we don't have a value
2026 const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2027 if (!pszValue || !*pszValue) return a_nDefault;
2028
2029 // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2030 char szValue[64] = { 0 };
2031 SI_CONVERTER c(m_bStoreIsUtf8);
2032 if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2033 return a_nDefault;
2034 }
2035
2036 // handle the value as hex if prefaced with "0x"
2037 long nValue = a_nDefault;
2038 char * pszSuffix = szValue;
2039 if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
2040 if (!szValue[2]) return a_nDefault;
2041 nValue = strtol(&szValue[2], &pszSuffix, 16);
2042 }
2043 else {
2044 nValue = strtol(szValue, &pszSuffix, 10);
2045 }
2046
2047 // any invalid strings will return the default value
2048 if (*pszSuffix) {
2049 return a_nDefault;
2050 }
2051
2052 return nValue;
2053}
2054
2055template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2056SI_Error
2057CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue(
2058 const SI_CHAR * a_pSection,
2059 const SI_CHAR * a_pKey,
2060 long a_nValue,
2061 const SI_CHAR * a_pComment,
2062 bool a_bUseHex,
2063 bool a_bForceReplace
2064 )
2065{
2066 // use SetValue to create sections
2067 if (!a_pSection || !a_pKey) return SI_FAIL;
2068
2069 // convert to an ASCII string
2070 char szInput[64];
2071#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2072 sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2073#else // !__STDC_WANT_SECURE_LIB__
2074 sprintf(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2075#endif // __STDC_WANT_SECURE_LIB__
2076
2077 // convert to output text
2078 SI_CHAR szOutput[64];
2079 SI_CONVERTER c(m_bStoreIsUtf8);
2080 c.ConvertFromStore(szInput, strlen(szInput) + 1,
2081 szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2082
2083 // actually add it
2084 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2085}
2086
2087template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2088double
2089CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetDoubleValue(
2090 const SI_CHAR * a_pSection,
2091 const SI_CHAR * a_pKey,
2092 double a_nDefault,
2093 bool * a_pHasMultiple
2094 ) const
2095{
2096 // return the default if we don't have a value
2097 const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2098 if (!pszValue || !*pszValue) return a_nDefault;
2099
2100 // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2101 char szValue[64] = { 0 };
2102 SI_CONVERTER c(m_bStoreIsUtf8);
2103 if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2104 return a_nDefault;
2105 }
2106
2107 char * pszSuffix = NULL;
2108 double nValue = strtod(szValue, &pszSuffix);
2109
2110 // any invalid strings will return the default value
2111 if (!pszSuffix || *pszSuffix) {
2112 return a_nDefault;
2113 }
2114
2115 return nValue;
2116}
2117
2118template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2119SI_Error
2120CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetDoubleValue(
2121 const SI_CHAR * a_pSection,
2122 const SI_CHAR * a_pKey,
2123 double a_nValue,
2124 const SI_CHAR * a_pComment,
2125 bool a_bForceReplace
2126 )
2127{
2128 // use SetValue to create sections
2129 if (!a_pSection || !a_pKey) return SI_FAIL;
2130
2131 // convert to an ASCII string
2132 char szInput[64];
2133#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2134 sprintf_s(szInput, "%f", a_nValue);
2135#else // !__STDC_WANT_SECURE_LIB__
2136 sprintf(szInput, "%f", a_nValue);
2137#endif // __STDC_WANT_SECURE_LIB__
2138
2139 // convert to output text
2140 SI_CHAR szOutput[64];
2141 SI_CONVERTER c(m_bStoreIsUtf8);
2142 c.ConvertFromStore(szInput, strlen(szInput) + 1,
2143 szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2144
2145 // actually add it
2146 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2147}
2148
2149template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2150bool
2151CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetBoolValue(
2152 const SI_CHAR * a_pSection,
2153 const SI_CHAR * a_pKey,
2154 bool a_bDefault,
2155 bool * a_pHasMultiple
2156 ) const
2157{
2158 // return the default if we don't have a value
2159 const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2160 if (!pszValue || !*pszValue) return a_bDefault;
2161
2162 // we only look at the minimum number of characters
2163 switch (pszValue[0]) {
2164 case 't': case 'T': // true
2165 case 'y': case 'Y': // yes
2166 case '1': // 1 (one)
2167 return true;
2168
2169 case 'f': case 'F': // false
2170 case 'n': case 'N': // no
2171 case '0': // 0 (zero)
2172 return false;
2173
2174 case 'o': case 'O':
2175 if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on
2176 if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off
2177 break;
2178 }
2179
2180 // no recognized value, return the default
2181 return a_bDefault;
2182}
2183
2184template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2185SI_Error
2186CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetBoolValue(
2187 const SI_CHAR * a_pSection,
2188 const SI_CHAR * a_pKey,
2189 bool a_bValue,
2190 const SI_CHAR * a_pComment,
2191 bool a_bForceReplace
2192 )
2193{
2194 // use SetValue to create sections
2195 if (!a_pSection || !a_pKey) return SI_FAIL;
2196
2197 // convert to an ASCII string
2198 const char * pszInput = a_bValue ? "true" : "false";
2199
2200 // convert to output text
2201 SI_CHAR szOutput[64];
2202 SI_CONVERTER c(m_bStoreIsUtf8);
2203 c.ConvertFromStore(pszInput, strlen(pszInput) + 1,
2204 szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2205
2206 // actually add it
2207 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2208}
2209
2210template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2211bool
2212CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues(
2213 const SI_CHAR * a_pSection,
2214 const SI_CHAR * a_pKey,
2215 TNamesDepend & a_values
2216 ) const
2217{
2218 a_values.clear();
2219
2220 if (!a_pSection || !a_pKey) {
2221 return false;
2222 }
2223 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2224 if (iSection == m_data.end()) {
2225 return false;
2226 }
2227 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2228 if (iKeyVal == iSection->second.end()) {
2229 return false;
2230 }
2231
2232 // insert all values for this key
2233 a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2234 if (m_bAllowMultiKey) {
2235 ++iKeyVal;
2236 while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {
2237 a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2238 ++iKeyVal;
2239 }
2240 }
2241
2242 return true;
2243}
2244
2245template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2246int
2247CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSectionSize(
2248 const SI_CHAR * a_pSection
2249 ) const
2250{
2251 if (!a_pSection) {
2252 return -1;
2253 }
2254
2255 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2256 if (iSection == m_data.end()) {
2257 return -1;
2258 }
2259 const TKeyVal & section = iSection->second;
2260
2261 // if multi-key isn't permitted then the section size is
2262 // the number of keys that we have.
2263 if (!m_bAllowMultiKey || section.empty()) {
2264 return (int) section.size();
2265 }
2266
2267 // otherwise we need to count them
2268 int nCount = 0;
2269 const SI_CHAR * pLastKey = NULL;
2270 typename TKeyVal::const_iterator iKeyVal = section.begin();
2271 for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
2272 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2273 ++nCount;
2274 pLastKey = iKeyVal->first.pItem;
2275 }
2276 }
2277 return nCount;
2278}
2279
2280template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2281const typename CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::TKeyVal *
2282CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSection(
2283 const SI_CHAR * a_pSection
2284 ) const
2285{
2286 if (a_pSection) {
2287 typename TSection::const_iterator i = m_data.find(a_pSection);
2288 if (i != m_data.end()) {
2289 return &(i->second);
2290 }
2291 }
2292 return 0;
2293}
2294
2295template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2296void
2297CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections(
2298 TNamesDepend & a_names
2299 ) const
2300{
2301 a_names.clear();
2302 typename TSection::const_iterator i = m_data.begin();
2303 for (int n = 0; i != m_data.end(); ++i, ++n ) {
2304 a_names.push_back(i->first);
2305 }
2306}
2307
2308template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2309bool
2310CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllKeys(
2311 const SI_CHAR * a_pSection,
2312 TNamesDepend & a_names
2313 ) const
2314{
2315 a_names.clear();
2316
2317 if (!a_pSection) {
2318 return false;
2319 }
2320
2321 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2322 if (iSection == m_data.end()) {
2323 return false;
2324 }
2325
2326 const TKeyVal & section = iSection->second;
2327 const SI_CHAR * pLastKey = NULL;
2328 typename TKeyVal::const_iterator iKeyVal = section.begin();
2329 for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) {
2330 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2331 a_names.push_back(iKeyVal->first);
2332 pLastKey = iKeyVal->first.pItem;
2333 }
2334 }
2335
2336 return true;
2337}
2338
2339template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2340SI_Error
2341CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
2342 const char * a_pszFile,
2343 bool a_bAddSignature
2344 ) const
2345{
2346 FILE * fp = NULL;
2347#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2348 fopen_s(&fp, a_pszFile, "wb");
2349#else // !__STDC_WANT_SECURE_LIB__
2350 fp = fopen(a_pszFile, "wb");
2351#endif // __STDC_WANT_SECURE_LIB__
2352 if (!fp) return SI_FILE;
2353 SI_Error rc = SaveFile(fp, a_bAddSignature);
2354 fclose(fp);
2355 return rc;
2356}
2357
2358#ifdef SI_HAS_WIDE_FILE
2359template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2360SI_Error
2361CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
2362 const SI_WCHAR_T * a_pwszFile,
2363 bool a_bAddSignature
2364 ) const
2365{
2366#ifdef _WIN32
2367 FILE * fp = NULL;
2368#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2369 _wfopen_s(&fp, a_pwszFile, L"wb");
2370#else // !__STDC_WANT_SECURE_LIB__
2371 fp = _wfopen(a_pwszFile, L"wb");
2372#endif // __STDC_WANT_SECURE_LIB__
2373 if (!fp) return SI_FILE;
2374 SI_Error rc = SaveFile(fp, a_bAddSignature);
2375 fclose(fp);
2376 return rc;
2377#else // !_WIN32 (therefore SI_CONVERT_ICU)
2378 char szFile[256];
2379 u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
2380 return SaveFile(szFile, a_bAddSignature);
2381#endif // _WIN32
2382}
2383#endif // SI_HAS_WIDE_FILE
2384
2385template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2386SI_Error
2387CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
2388 FILE * a_pFile,
2389 bool a_bAddSignature
2390 ) const
2391{
2392 FileWriter writer(a_pFile);
2393 return Save(writer, a_bAddSignature);
2394}
2395
2396template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2397SI_Error
2398CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
2399 OutputWriter & a_oOutput,
2400 bool a_bAddSignature
2401 ) const
2402{
2403 Converter convert(m_bStoreIsUtf8);
2404
2405 // add the UTF-8 signature if it is desired
2406 if (m_bStoreIsUtf8 && a_bAddSignature) {
2407 a_oOutput.Write(SI_UTF8_SIGNATURE);
2408 }
2409
2410 // get all of the sections sorted in load order
2411 TNamesDepend oSections;
2412 GetAllSections(oSections);
2413#if defined(_MSC_VER) && _MSC_VER <= 1200
2414 oSections.sort();
2415#elif defined(__BORLANDC__)
2416 oSections.sort(Entry::LoadOrder());
2417#else
2418 oSections.sort(typename Entry::LoadOrder());
2419#endif
2420
2421 // write the file comment if we have one
2422 bool bNeedNewLine = false;
2423 if (m_pFileComment) {
2424 if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
2425 return SI_FAIL;
2426 }
2427 bNeedNewLine = true;
2428 }
2429
2430 // iterate through our sections and output the data
2431 typename TNamesDepend::const_iterator iSection = oSections.begin();
2432 for ( ; iSection != oSections.end(); ++iSection ) {
2433 // write out the comment if there is one
2434 if (iSection->pComment) {
2435 if (bNeedNewLine) {
2436 a_oOutput.Write(SI_NEWLINE_A);
2437 a_oOutput.Write(SI_NEWLINE_A);
2438 }
2439 if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) {
2440 return SI_FAIL;
2441 }
2442 bNeedNewLine = false;
2443 }
2444
2445 if (bNeedNewLine) {
2446 a_oOutput.Write(SI_NEWLINE_A);
2447 a_oOutput.Write(SI_NEWLINE_A);
2448 bNeedNewLine = false;
2449 }
2450
2451 // write the section (unless there is no section name)
2452 if (*iSection->pItem) {
2453 if (!convert.ConvertToStore(iSection->pItem)) {
2454 return SI_FAIL;
2455 }
2456 a_oOutput.Write("[");
2457 a_oOutput.Write(convert.Data());
2458 a_oOutput.Write("]");
2459 a_oOutput.Write(SI_NEWLINE_A);
2460 }
2461
2462 // get all of the keys sorted in load order
2463 TNamesDepend oKeys;
2464 GetAllKeys(iSection->pItem, oKeys);
2465#if defined(_MSC_VER) && _MSC_VER <= 1200
2466 oKeys.sort();
2467#elif defined(__BORLANDC__)
2468 oKeys.sort(Entry::LoadOrder());
2469#else
2470 oKeys.sort(typename Entry::LoadOrder());
2471#endif
2472
2473 // write all keys and values
2474 typename TNamesDepend::const_iterator iKey = oKeys.begin();
2475 for ( ; iKey != oKeys.end(); ++iKey) {
2476 // get all values for this key
2477 TNamesDepend oValues;
2478 GetAllValues(iSection->pItem, iKey->pItem, oValues);
2479
2480 typename TNamesDepend::const_iterator iValue = oValues.begin();
2481 for ( ; iValue != oValues.end(); ++iValue) {
2482 // write out the comment if there is one
2483 if (iValue->pComment) {
2484 a_oOutput.Write(SI_NEWLINE_A);
2485 if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) {
2486 return SI_FAIL;
2487 }
2488 }
2489
2490 // write the key
2491 if (!convert.ConvertToStore(iKey->pItem)) {
2492 return SI_FAIL;
2493 }
2494 a_oOutput.Write(convert.Data());
2495
2496 // write the value
2497 if (!convert.ConvertToStore(iValue->pItem)) {
2498 return SI_FAIL;
2499 }
2500 a_oOutput.Write(m_bSpaces ? " = " : "=");
2501 if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
2502 // multi-line data needs to be processed specially to ensure
2503 // that we use the correct newline format for the current system
2504 a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
2505 if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
2506 return SI_FAIL;
2507 }
2508 a_oOutput.Write("END_OF_TEXT");
2509 }
2510 else {
2511 a_oOutput.Write(convert.Data());
2512 }
2513 a_oOutput.Write(SI_NEWLINE_A);
2514 }
2515 }
2516
2517 bNeedNewLine = true;
2518 }
2519
2520 return SI_OK;
2521}
2522
2523template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2524bool
2525CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::OutputMultiLineText(
2526 OutputWriter & a_oOutput,
2527 Converter & a_oConverter,
2528 const SI_CHAR * a_pText
2529 ) const
2530{
2531 const SI_CHAR * pEndOfLine;
2532 SI_CHAR cEndOfLineChar = *a_pText;
2533 while (cEndOfLineChar) {
2534 // find the end of this line
2535 pEndOfLine = a_pText;
2536 for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;
2537 cEndOfLineChar = *pEndOfLine;
2538
2539 // temporarily null terminate, convert and output the line
2540 *const_cast<SI_CHAR*>(pEndOfLine) = 0;
2541 if (!a_oConverter.ConvertToStore(a_pText)) {
2542 return false;
2543 }
2544 *const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar;
2545 a_pText += (pEndOfLine - a_pText) + 1;
2546 a_oOutput.Write(a_oConverter.Data());
2547 a_oOutput.Write(SI_NEWLINE_A);
2548 }
2549 return true;
2550}
2551
2552template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2553bool
2554CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(
2555 const SI_CHAR * a_pSection,
2556 const SI_CHAR * a_pKey,
2557 bool a_bRemoveEmpty
2558 )
2559{
2560 return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty);
2561}
2562
2563template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2564bool
2565CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteValue(
2566 const SI_CHAR * a_pSection,
2567 const SI_CHAR * a_pKey,
2568 const SI_CHAR * a_pValue,
2569 bool a_bRemoveEmpty
2570 )
2571{
2572 if (!a_pSection) {
2573 return false;
2574 }
2575
2576 typename TSection::iterator iSection = m_data.find(a_pSection);
2577 if (iSection == m_data.end()) {
2578 return false;
2579 }
2580
2581 // remove a single key if we have a keyname
2582 if (a_pKey) {
2583 typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
2584 if (iKeyVal == iSection->second.end()) {
2585 return false;
2586 }
2587
2588 const static SI_STRLESS isLess = SI_STRLESS();
2589
2590 // remove any copied strings and then the key
2591 typename TKeyVal::iterator iDelete;
2592 bool bDeleted = false;
2593 do {
2594 iDelete = iKeyVal++;
2595
2596 if(a_pValue == NULL ||
2597 (isLess(a_pValue, iDelete->second) == false &&
2598 isLess(iDelete->second, a_pValue) == false)) {
2599 DeleteString(iDelete->first.pItem);
2600 DeleteString(iDelete->second);
2601 iSection->second.erase(iDelete);
2602 bDeleted = true;
2603 }
2604 }
2605 while (iKeyVal != iSection->second.end()
2606 && !IsLess(a_pKey, iKeyVal->first.pItem));
2607
2608 if(!bDeleted) {
2609 return false;
2610 }
2611
2612 // done now if the section is not empty or we are not pruning away
2613 // the empty sections. Otherwise let it fall through into the section
2614 // deletion code
2615 if (!a_bRemoveEmpty || !iSection->second.empty()) {
2616 return true;
2617 }
2618 }
2619 else {
2620 // delete all copied strings from this section. The actual
2621 // entries will be removed when the section is removed.
2622 typename TKeyVal::iterator iKeyVal = iSection->second.begin();
2623 for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
2624 DeleteString(iKeyVal->first.pItem);
2625 DeleteString(iKeyVal->second);
2626 }
2627 }
2628
2629 // delete the section itself
2630 DeleteString(iSection->first.pItem);
2631 m_data.erase(iSection);
2632
2633 return true;
2634}
2635
2636template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2637void
2638CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString(
2639 const SI_CHAR * a_pString
2640 )
2641{
2642 // strings may exist either inside the data block, or they will be
2643 // individually allocated and stored in m_strings. We only physically
2644 // delete those stored in m_strings.
2645 if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
2646 typename TNamesDepend::iterator i = m_strings.begin();
2647 for (;i != m_strings.end(); ++i) {
2648 if (a_pString == i->pItem) {
2649 delete[] const_cast<SI_CHAR*>(i->pItem);
2650 m_strings.erase(i);
2651 break;
2652 }
2653 }
2654 }
2655}
2656
2657// ---------------------------------------------------------------------------
2658// CONVERSION FUNCTIONS
2659// ---------------------------------------------------------------------------
2660
2661// Defines the conversion classes for different libraries. Before including
2662// SimpleIni.h, set the converter that you wish you use by defining one of the
2663// following symbols.
2664//
2665// SI_CONVERT_GENERIC Use the Unicode reference conversion library in
2666// the accompanying files ConvertUTF.h/c
2667// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
2668// ICU headers on include path and icuuc.lib
2669// SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
2670
2671#if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
2672# ifdef _WIN32
2673# define SI_CONVERT_WIN32
2674# else
2675# define SI_CONVERT_GENERIC
2676# endif
2677#endif
2678
2679/**
2680 * Generic case-sensitive less than comparison. This class returns numerically
2681 * ordered ASCII case-sensitive text for all possible sizes and types of
2682 * SI_CHAR.
2683 */
2684template<class SI_CHAR>
2685struct SI_GenericCase {
2686 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2687 long cmp;
2688 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2689 cmp = (long) *pLeft - (long) *pRight;
2690 if (cmp != 0) {
2691 return cmp < 0;
2692 }
2693 }
2694 return *pRight != 0;
2695 }
2696};
2697
2698/**
2699 * Generic ASCII case-insensitive less than comparison. This class returns
2700 * numerically ordered ASCII case-insensitive text for all possible sizes
2701 * and types of SI_CHAR. It is not safe for MBCS text comparison where
2702 * ASCII A-Z characters are used in the encoding of multi-byte characters.
2703 */
2704template<class SI_CHAR>
2705struct SI_GenericNoCase {
2706 inline SI_CHAR locase(SI_CHAR ch) const {
2707 return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
2708 }
2709 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2710 long cmp;
2711 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2712 cmp = (long) locase(*pLeft) - (long) locase(*pRight);
2713 if (cmp != 0) {
2714 return cmp < 0;
2715 }
2716 }
2717 return *pRight != 0;
2718 }
2719};
2720
2721/**
2722 * Null conversion class for MBCS/UTF-8 to char (or equivalent).
2723 */
2724template<class SI_CHAR>
2725class SI_ConvertA {
2726 bool m_bStoreIsUtf8;
2727protected:
2728 SI_ConvertA() { }
2729public:
2730 SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
2731
2732 /* copy and assignment */
2733 SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }
2734 SI_ConvertA & operator=(const SI_ConvertA & rhs) {
2735 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2736 return *this;
2737 }
2738
2739 /** Calculate the number of SI_CHAR required for converting the input
2740 * from the storage format. The storage format is always UTF-8 or MBCS.
2741 *
2742 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2743 * @param a_uInputDataLen Length of storage format data in bytes. This
2744 * must be the actual length of the data, including
2745 * NULL byte if NULL terminated string is required.
2746 * @return Number of SI_CHAR required by the string when
2747 * converted. If there are embedded NULL bytes in the
2748 * input data, only the string up and not including
2749 * the NULL byte will be converted.
2750 * @return -1 cast to size_t on a conversion error.
2751 */
2752 size_t SizeFromStore(
2753 const char * a_pInputData,
2754 size_t a_uInputDataLen)
2755 {
2756 (void)a_pInputData;
2757 SI_ASSERT(a_uInputDataLen != (size_t) -1);
2758
2759 // ASCII/MBCS/UTF-8 needs no conversion
2760 return a_uInputDataLen;
2761 }
2762
2763 /** Convert the input string from the storage format to SI_CHAR.
2764 * The storage format is always UTF-8 or MBCS.
2765 *
2766 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2767 * @param a_uInputDataLen Length of storage format data in bytes. This
2768 * must be the actual length of the data, including
2769 * NULL byte if NULL terminated string is required.
2770 * @param a_pOutputData Pointer to the output buffer to received the
2771 * converted data.
2772 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2773 * @return true if all of the input data was successfully
2774 * converted.
2775 */
2776 bool ConvertFromStore(
2777 const char * a_pInputData,
2778 size_t a_uInputDataLen,
2779 SI_CHAR * a_pOutputData,
2780 size_t a_uOutputDataSize)
2781 {
2782 // ASCII/MBCS/UTF-8 needs no conversion
2783 if (a_uInputDataLen > a_uOutputDataSize) {
2784 return false;
2785 }
2786 memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
2787 return true;
2788 }
2789
2790 /** Calculate the number of char required by the storage format of this
2791 * data. The storage format is always UTF-8 or MBCS.
2792 *
2793 * @param a_pInputData NULL terminated string to calculate the number of
2794 * bytes required to be converted to storage format.
2795 * @return Number of bytes required by the string when
2796 * converted to storage format. This size always
2797 * includes space for the terminating NULL character.
2798 * @return -1 cast to size_t on a conversion error.
2799 */
2800 size_t SizeToStore(
2801 const SI_CHAR * a_pInputData)
2802 {
2803 // ASCII/MBCS/UTF-8 needs no conversion
2804 return strlen((const char *)a_pInputData) + 1;
2805 }
2806
2807 /** Convert the input string to the storage format of this data.
2808 * The storage format is always UTF-8 or MBCS.
2809 *
2810 * @param a_pInputData NULL terminated source string to convert. All of
2811 * the data will be converted including the
2812 * terminating NULL character.
2813 * @param a_pOutputData Pointer to the buffer to receive the converted
2814 * string.
2815 * @param a_uOutputDataSize Size of the output buffer in char.
2816 * @return true if all of the input data, including the
2817 * terminating NULL character was successfully
2818 * converted.
2819 */
2820 bool ConvertToStore(
2821 const SI_CHAR * a_pInputData,
2822 char * a_pOutputData,
2823 size_t a_uOutputDataSize)
2824 {
2825 // calc input string length (SI_CHAR type and size independent)
2826 size_t uInputLen = strlen((const char *)a_pInputData) + 1;
2827 if (uInputLen > a_uOutputDataSize) {
2828 return false;
2829 }
2830
2831 // ascii/UTF-8 needs no conversion
2832 memcpy(a_pOutputData, a_pInputData, uInputLen);
2833 return true;
2834 }
2835};
2836
2837
2838// ---------------------------------------------------------------------------
2839// SI_CONVERT_GENERIC
2840// ---------------------------------------------------------------------------
2841#ifdef SI_CONVERT_GENERIC
2842
2843#define SI_Case SI_GenericCase
2844#define SI_NoCase SI_GenericNoCase
2845
2846#include <wchar.h>
2847#include "ConvertUTF.h"
2848
2849/**
2850 * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference
2851 * library functions. This can be used on all platforms.
2852 */
2853template<class SI_CHAR>
2854class SI_ConvertW {
2855 bool m_bStoreIsUtf8;
2856protected:
2857 SI_ConvertW() { }
2858public:
2859 SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
2860
2861 /* copy and assignment */
2862 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
2863 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
2864 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2865 return *this;
2866 }
2867
2868 /** Calculate the number of SI_CHAR required for converting the input
2869 * from the storage format. The storage format is always UTF-8 or MBCS.
2870 *
2871 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2872 * @param a_uInputDataLen Length of storage format data in bytes. This
2873 * must be the actual length of the data, including
2874 * NULL byte if NULL terminated string is required.
2875 * @return Number of SI_CHAR required by the string when
2876 * converted. If there are embedded NULL bytes in the
2877 * input data, only the string up and not including
2878 * the NULL byte will be converted.
2879 * @return -1 cast to size_t on a conversion error.
2880 */
2881 size_t SizeFromStore(
2882 const char * a_pInputData,
2883 size_t a_uInputDataLen)
2884 {
2885 SI_ASSERT(a_uInputDataLen != (size_t) -1);
2886
2887 if (m_bStoreIsUtf8) {
2888 // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
2889 // so we just return the same number of characters required as for
2890 // the source text.
2891 return a_uInputDataLen;
2892 }
2893
2894#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux))
2895 // fall back processing for platforms that don't support a NULL dest to mbstowcs
2896 // worst case scenario is 1:1, this will be a sufficient buffer size
2897 (void)a_pInputData;
2898 return a_uInputDataLen;
2899#else
2900 // get the actual required buffer size
2901 return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
2902#endif
2903 }
2904
2905 /** Convert the input string from the storage format to SI_CHAR.
2906 * The storage format is always UTF-8 or MBCS.
2907 *
2908 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2909 * @param a_uInputDataLen Length of storage format data in bytes. This
2910 * must be the actual length of the data, including
2911 * NULL byte if NULL terminated string is required.
2912 * @param a_pOutputData Pointer to the output buffer to received the
2913 * converted data.
2914 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2915 * @return true if all of the input data was successfully
2916 * converted.
2917 */
2918 bool ConvertFromStore(
2919 const char * a_pInputData,
2920 size_t a_uInputDataLen,
2921 SI_CHAR * a_pOutputData,
2922 size_t a_uOutputDataSize)
2923 {
2924 if (m_bStoreIsUtf8) {
2925 // This uses the Unicode reference implementation to do the
2926 // conversion from UTF-8 to wchar_t. The required files are
2927 // ConvertUTF.h and ConvertUTF.c which should be included in
2928 // the distribution but are publically available from unicode.org
2929 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
2930 ConversionResult retval;
2931 const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
2932 if (sizeof(wchar_t) == sizeof(UTF32)) {
2933 UTF32 * pUtf32 = (UTF32 *) a_pOutputData;
2934 retval = ConvertUTF8toUTF32(
2935 &pUtf8, pUtf8 + a_uInputDataLen,
2936 &pUtf32, pUtf32 + a_uOutputDataSize,
2937 lenientConversion);
2938 }
2939 else if (sizeof(wchar_t) == sizeof(UTF16)) {
2940 UTF16 * pUtf16 = (UTF16 *) a_pOutputData;
2941 retval = ConvertUTF8toUTF16(
2942 &pUtf8, pUtf8 + a_uInputDataLen,
2943 &pUtf16, pUtf16 + a_uOutputDataSize,
2944 lenientConversion);
2945 }
2946 return retval == conversionOK;
2947 }
2948
2949 // convert to wchar_t
2950 size_t retval = mbstowcs(a_pOutputData,
2951 a_pInputData, a_uOutputDataSize);
2952 return retval != (size_t)(-1);
2953 }
2954
2955 /** Calculate the number of char required by the storage format of this
2956 * data. The storage format is always UTF-8 or MBCS.
2957 *
2958 * @param a_pInputData NULL terminated string to calculate the number of
2959 * bytes required to be converted to storage format.
2960 * @return Number of bytes required by the string when
2961 * converted to storage format. This size always
2962 * includes space for the terminating NULL character.
2963 * @return -1 cast to size_t on a conversion error.
2964 */
2965 size_t SizeToStore(
2966 const SI_CHAR * a_pInputData)
2967 {
2968 if (m_bStoreIsUtf8) {
2969 // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
2970 size_t uLen = 0;
2971 while (a_pInputData[uLen]) {
2972 ++uLen;
2973 }
2974 return (6 * uLen) + 1;
2975 }
2976 else {
2977 size_t uLen = wcstombs(NULL, a_pInputData, 0);
2978 if (uLen == (size_t)(-1)) {
2979 return uLen;
2980 }
2981 return uLen + 1; // include NULL terminator
2982 }
2983 }
2984
2985 /** Convert the input string to the storage format of this data.
2986 * The storage format is always UTF-8 or MBCS.
2987 *
2988 * @param a_pInputData NULL terminated source string to convert. All of
2989 * the data will be converted including the
2990 * terminating NULL character.
2991 * @param a_pOutputData Pointer to the buffer to receive the converted
2992 * string.
2993 * @param a_uOutputDataSize Size of the output buffer in char.
2994 * @return true if all of the input data, including the
2995 * terminating NULL character was successfully
2996 * converted.
2997 */
2998 bool ConvertToStore(
2999 const SI_CHAR * a_pInputData,
3000 char * a_pOutputData,
3001 size_t a_uOutputDataSize
3002 )
3003 {
3004 if (m_bStoreIsUtf8) {
3005 // calc input string length (SI_CHAR type and size independent)
3006 size_t uInputLen = 0;
3007 while (a_pInputData[uInputLen]) {
3008 ++uInputLen;
3009 }
3010 ++uInputLen; // include the NULL char
3011
3012 // This uses the Unicode reference implementation to do the
3013 // conversion from wchar_t to UTF-8. The required files are
3014 // ConvertUTF.h and ConvertUTF.c which should be included in
3015 // the distribution but are publically available from unicode.org
3016 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
3017 ConversionResult retval;
3018 UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
3019 if (sizeof(wchar_t) == sizeof(UTF32)) {
3020 const UTF32 * pUtf32 = (const UTF32 *) a_pInputData;
3021 retval = ConvertUTF32toUTF8(
3022 &pUtf32, pUtf32 + uInputLen,
3023 &pUtf8, pUtf8 + a_uOutputDataSize,
3024 lenientConversion);
3025 }
3026 else if (sizeof(wchar_t) == sizeof(UTF16)) {
3027 const UTF16 * pUtf16 = (const UTF16 *) a_pInputData;
3028 retval = ConvertUTF16toUTF8(
3029 &pUtf16, pUtf16 + uInputLen,
3030 &pUtf8, pUtf8 + a_uOutputDataSize,
3031 lenientConversion);
3032 }
3033 return retval == conversionOK;
3034 }
3035 else {
3036 size_t retval = wcstombs(a_pOutputData,
3037 a_pInputData, a_uOutputDataSize);
3038 return retval != (size_t) -1;
3039 }
3040 }
3041};
3042
3043#endif // SI_CONVERT_GENERIC
3044
3045
3046// ---------------------------------------------------------------------------
3047// SI_CONVERT_ICU
3048// ---------------------------------------------------------------------------
3049#ifdef SI_CONVERT_ICU
3050
3051#define SI_Case SI_GenericCase
3052#define SI_NoCase SI_GenericNoCase
3053
3054#include <unicode/ucnv.h>
3055
3056/**
3057 * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms.
3058 */
3059template<class SI_CHAR>
3060class SI_ConvertW {
3061 const char * m_pEncoding;
3062 UConverter * m_pConverter;
3063protected:
3064 SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
3065public:
3066 SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
3067 m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
3068 }
3069
3070 /* copy and assignment */
3071 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
3072 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
3073 m_pEncoding = rhs.m_pEncoding;
3074 m_pConverter = NULL;
3075 return *this;
3076 }
3077 ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); }
3078
3079 /** Calculate the number of UChar required for converting the input
3080 * from the storage format. The storage format is always UTF-8 or MBCS.
3081 *
3082 * @param a_pInputData Data in storage format to be converted to UChar.
3083 * @param a_uInputDataLen Length of storage format data in bytes. This
3084 * must be the actual length of the data, including
3085 * NULL byte if NULL terminated string is required.
3086 * @return Number of UChar required by the string when
3087 * converted. If there are embedded NULL bytes in the
3088 * input data, only the string up and not including
3089 * the NULL byte will be converted.
3090 * @return -1 cast to size_t on a conversion error.
3091 */
3092 size_t SizeFromStore(
3093 const char * a_pInputData,
3094 size_t a_uInputDataLen)
3095 {
3096 SI_ASSERT(a_uInputDataLen != (size_t) -1);
3097
3098 UErrorCode nError;
3099
3100 if (!m_pConverter) {
3101 nError = U_ZERO_ERROR;
3102 m_pConverter = ucnv_open(m_pEncoding, &nError);
3103 if (U_FAILURE(nError)) {
3104 return (size_t) -1;
3105 }
3106 }
3107
3108 nError = U_ZERO_ERROR;
3109 int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,
3110 a_pInputData, (int32_t) a_uInputDataLen, &nError);
3111 if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3112 return (size_t) -1;
3113 }
3114
3115 return (size_t) nLen;
3116 }
3117
3118 /** Convert the input string from the storage format to UChar.
3119 * The storage format is always UTF-8 or MBCS.
3120 *
3121 * @param a_pInputData Data in storage format to be converted to UChar.
3122 * @param a_uInputDataLen Length of storage format data in bytes. This
3123 * must be the actual length of the data, including
3124 * NULL byte if NULL terminated string is required.
3125 * @param a_pOutputData Pointer to the output buffer to received the
3126 * converted data.
3127 * @param a_uOutputDataSize Size of the output buffer in UChar.
3128 * @return true if all of the input data was successfully
3129 * converted.
3130 */
3131 bool ConvertFromStore(
3132 const char * a_pInputData,
3133 size_t a_uInputDataLen,
3134 UChar * a_pOutputData,
3135 size_t a_uOutputDataSize)
3136 {
3137 UErrorCode nError;
3138
3139 if (!m_pConverter) {
3140 nError = U_ZERO_ERROR;
3141 m_pConverter = ucnv_open(m_pEncoding, &nError);
3142 if (U_FAILURE(nError)) {
3143 return false;
3144 }
3145 }
3146
3147 nError = U_ZERO_ERROR;
3148 ucnv_toUChars(m_pConverter,
3149 a_pOutputData, (int32_t) a_uOutputDataSize,
3150 a_pInputData, (int32_t) a_uInputDataLen, &nError);
3151 if (U_FAILURE(nError)) {
3152 return false;
3153 }
3154
3155 return true;
3156 }
3157
3158 /** Calculate the number of char required by the storage format of this
3159 * data. The storage format is always UTF-8 or MBCS.
3160 *
3161 * @param a_pInputData NULL terminated string to calculate the number of
3162 * bytes required to be converted to storage format.
3163 * @return Number of bytes required by the string when
3164 * converted to storage format. This size always
3165 * includes space for the terminating NULL character.
3166 * @return -1 cast to size_t on a conversion error.
3167 */
3168 size_t SizeToStore(
3169 const UChar * a_pInputData)
3170 {
3171 UErrorCode nError;
3172
3173 if (!m_pConverter) {
3174 nError = U_ZERO_ERROR;
3175 m_pConverter = ucnv_open(m_pEncoding, &nError);
3176 if (U_FAILURE(nError)) {
3177 return (size_t) -1;
3178 }
3179 }
3180
3181 nError = U_ZERO_ERROR;
3182 int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,
3183 a_pInputData, -1, &nError);
3184 if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3185 return (size_t) -1;
3186 }
3187
3188 return (size_t) nLen + 1;
3189 }
3190
3191 /** Convert the input string to the storage format of this data.
3192 * The storage format is always UTF-8 or MBCS.
3193 *
3194 * @param a_pInputData NULL terminated source string to convert. All of
3195 * the data will be converted including the
3196 * terminating NULL character.
3197 * @param a_pOutputData Pointer to the buffer to receive the converted
3198 * string.
3199 * @param a_pOutputDataSize Size of the output buffer in char.
3200 * @return true if all of the input data, including the
3201 * terminating NULL character was successfully
3202 * converted.
3203 */
3204 bool ConvertToStore(
3205 const UChar * a_pInputData,
3206 char * a_pOutputData,
3207 size_t a_uOutputDataSize)
3208 {
3209 UErrorCode nError;
3210
3211 if (!m_pConverter) {
3212 nError = U_ZERO_ERROR;
3213 m_pConverter = ucnv_open(m_pEncoding, &nError);
3214 if (U_FAILURE(nError)) {
3215 return false;
3216 }
3217 }
3218
3219 nError = U_ZERO_ERROR;
3220 ucnv_fromUChars(m_pConverter,
3221 a_pOutputData, (int32_t) a_uOutputDataSize,
3222 a_pInputData, -1, &nError);
3223 if (U_FAILURE(nError)) {
3224 return false;
3225 }
3226
3227 return true;
3228 }
3229};
3230
3231#endif // SI_CONVERT_ICU
3232
3233
3234// ---------------------------------------------------------------------------
3235// SI_CONVERT_WIN32
3236// ---------------------------------------------------------------------------
3237#ifdef SI_CONVERT_WIN32
3238
3239#define SI_Case SI_GenericCase
3240
3241// Windows CE doesn't have errno or MBCS libraries
3242#ifdef _WIN32_WCE
3243# ifndef SI_NO_MBCS
3244# define SI_NO_MBCS
3245# endif
3246#endif
3247
3248#include <windows.h>
3249#ifdef SI_NO_MBCS
3250# define SI_NoCase SI_GenericNoCase
3251#else // !SI_NO_MBCS
3252/**
3253 * Case-insensitive comparison class using Win32 MBCS functions. This class
3254 * returns a case-insensitive semi-collation order for MBCS text. It may not
3255 * be safe for UTF-8 text returned in char format as we don't know what
3256 * characters will be folded by the function! Therefore, if you are using
3257 * SI_CHAR == char and SetUnicode(true), then you need to use the generic
3258 * SI_NoCase class instead.
3259 */
3260#include <mbstring.h>
3261template<class SI_CHAR>
3262struct SI_NoCase {
3263 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
3264 if (sizeof(SI_CHAR) == sizeof(char)) {
3265 return _mbsicmp((const unsigned char *)pLeft,
3266 (const unsigned char *)pRight) < 0;
3267 }
3268 if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
3269 return _wcsicmp((const wchar_t *)pLeft,
3270 (const wchar_t *)pRight) < 0;
3271 }
3272 return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight);
3273 }
3274};
3275#endif // SI_NO_MBCS
3276
3277/**
3278 * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses
3279 * only the Win32 functions and doesn't require the external Unicode UTF-8
3280 * conversion library. It will not work on Windows 95 without using Microsoft
3281 * Layer for Unicode in your application.
3282 */
3283template<class SI_CHAR>
3284class SI_ConvertW {
3285 UINT m_uCodePage;
3286protected:
3287 SI_ConvertW() { }
3288public:
3289 SI_ConvertW(bool a_bStoreIsUtf8) {
3290 m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP;
3291 }
3292
3293 /* copy and assignment */
3294 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
3295 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
3296 m_uCodePage = rhs.m_uCodePage;
3297 return *this;
3298 }
3299
3300 /** Calculate the number of SI_CHAR required for converting the input
3301 * from the storage format. The storage format is always UTF-8 or MBCS.
3302 *
3303 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
3304 * @param a_uInputDataLen Length of storage format data in bytes. This
3305 * must be the actual length of the data, including
3306 * NULL byte if NULL terminated string is required.
3307 * @return Number of SI_CHAR required by the string when
3308 * converted. If there are embedded NULL bytes in the
3309 * input data, only the string up and not including
3310 * the NULL byte will be converted.
3311 * @return -1 cast to size_t on a conversion error.
3312 */
3313 size_t SizeFromStore(
3314 const char * a_pInputData,
3315 size_t a_uInputDataLen)
3316 {
3317 SI_ASSERT(a_uInputDataLen != (size_t) -1);
3318
3319 int retval = MultiByteToWideChar(
3320 m_uCodePage, 0,
3321 a_pInputData, (int) a_uInputDataLen,
3322 0, 0);
3323 return (size_t)(retval > 0 ? retval : -1);
3324 }
3325
3326 /** Convert the input string from the storage format to SI_CHAR.
3327 * The storage format is always UTF-8 or MBCS.
3328 *
3329 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
3330 * @param a_uInputDataLen Length of storage format data in bytes. This
3331 * must be the actual length of the data, including
3332 * NULL byte if NULL terminated string is required.
3333 * @param a_pOutputData Pointer to the output buffer to received the
3334 * converted data.
3335 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
3336 * @return true if all of the input data was successfully
3337 * converted.
3338 */
3339 bool ConvertFromStore(
3340 const char * a_pInputData,
3341 size_t a_uInputDataLen,
3342 SI_CHAR * a_pOutputData,
3343 size_t a_uOutputDataSize)
3344 {
3345 int nSize = MultiByteToWideChar(
3346 m_uCodePage, 0,
3347 a_pInputData, (int) a_uInputDataLen,
3348 (wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
3349 return (nSize > 0);
3350 }
3351
3352 /** Calculate the number of char required by the storage format of this
3353 * data. The storage format is always UTF-8.
3354 *
3355 * @param a_pInputData NULL terminated string to calculate the number of
3356 * bytes required to be converted to storage format.
3357 * @return Number of bytes required by the string when
3358 * converted to storage format. This size always
3359 * includes space for the terminating NULL character.
3360 * @return -1 cast to size_t on a conversion error.
3361 */
3362 size_t SizeToStore(
3363 const SI_CHAR * a_pInputData)
3364 {
3365 int retval = WideCharToMultiByte(
3366 m_uCodePage, 0,
3367 (const wchar_t *) a_pInputData, -1,
3368 0, 0, 0, 0);
3369 return (size_t) (retval > 0 ? retval : -1);
3370 }
3371
3372 /** Convert the input string to the storage format of this data.
3373 * The storage format is always UTF-8 or MBCS.
3374 *
3375 * @param a_pInputData NULL terminated source string to convert. All of
3376 * the data will be converted including the
3377 * terminating NULL character.
3378 * @param a_pOutputData Pointer to the buffer to receive the converted
3379 * string.
3380 * @param a_pOutputDataSize Size of the output buffer in char.
3381 * @return true if all of the input data, including the
3382 * terminating NULL character was successfully
3383 * converted.
3384 */
3385 bool ConvertToStore(
3386 const SI_CHAR * a_pInputData,
3387 char * a_pOutputData,
3388 size_t a_uOutputDataSize)
3389 {
3390 int retval = WideCharToMultiByte(
3391 m_uCodePage, 0,
3392 (const wchar_t *) a_pInputData, -1,
3393 a_pOutputData, (int) a_uOutputDataSize, 0, 0);
3394 return retval > 0;
3395 }
3396};
3397
3398#endif // SI_CONVERT_WIN32
3399
3400
3401// ---------------------------------------------------------------------------
3402// TYPE DEFINITIONS
3403// ---------------------------------------------------------------------------
3404
3405typedef CSimpleIniTempl<char,
3406 SI_NoCase<char>,SI_ConvertA<char> > CSimpleIniA;
3407typedef CSimpleIniTempl<char,
3408 SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA;
3409
3410#if defined(SI_CONVERT_ICU)
3411typedef CSimpleIniTempl<UChar,
3412 SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;
3413typedef CSimpleIniTempl<UChar,
3414 SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;
3415#else
3416typedef CSimpleIniTempl<wchar_t,
3417 SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;
3418typedef CSimpleIniTempl<wchar_t,
3419 SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;
3420#endif
3421
3422#ifdef _UNICODE
3423# define CSimpleIni CSimpleIniW
3424# define CSimpleIniCase CSimpleIniCaseW
3425# define SI_NEWLINE SI_NEWLINE_W
3426#else // !_UNICODE
3427# define CSimpleIni CSimpleIniA
3428# define CSimpleIniCase CSimpleIniCaseA
3429# define SI_NEWLINE SI_NEWLINE_A
3430#endif // _UNICODE
3431
3432#ifdef _MSC_VER
3433# pragma warning (pop)
3434#endif
3435
3436#endif // INCLUDED_SimpleIni_h
3437
3438