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