annotate src/Support/SimpleIni.h @ 46:c8024714e13e

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