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