annotate trunk/src/Support/SimpleIni.h @ 706:f8e90b5d85fd tip

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