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