3 // Author: Brodie Thiesfield <code@jellycan.com>
4 // Source: http://code.jellycan.com/simpleini/
9 // This component allows an INI-style configuration file to be used on both
10 // Windows and Linux/Unix. It is fast, simple and source code using this
11 // component will compile unchanged on either OS.
15 // * MIT Licence allows free use in all software (including GPL and commercial)
16 // * multi-platform (Windows 95/98/ME/NT/2K/XP/2003, Windows CE, Linux, Unix)
17 // * loading and saving of INI-style configuration files
18 // * configuration files can have any newline format on all platforms
19 // * liberal acceptance of file format
20 // - key/values with no section
21 // - removal of whitespace around sections, keys and values
22 // * support for multi-line values (values with embedded newline characters)
23 // * optional support for multiple keys with the same name
24 // * optional case-insensitive sections and keys (for ASCII characters only)
25 // * supports both char or wchar_t programming interfaces
26 // * supports both MBCS (system locale) and UTF-8 file encodings
27 // * system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file
28 // * support for non-ASCII characters in section, keys, values and comments
29 // * support for non-standard character types or file encodings
30 // via user-written converter classes
31 // * support for adding/modifying values programmatically
32 // * compiles cleanly at warning level 4 (Windows/VC.NET 2003), warning level
33 // 3 (Windows/VC6) and -Wall (Linux/gcc)
37 // 1. Define the appropriate symbol for the converter you wish to use and
38 // include the SimpleIni.h header file. If no specific converter is defined
39 // then the default converter is used. The default conversion mode uses
40 // SI_CONVERT_WIN32 on Windows and SI_CONVERT_GENERIC on all other
41 // platforms. If you are using ICU then SI_CONVERT_ICU is supported on all
44 // 2. Declare an instance the appropriate class. Note that the following
45 // definitions are just shortcuts for commonly used types. Other types
46 // (PRUnichar, unsigned short, unsigned char) are also possible.
48 // Interface Case-sensitive Load UTF-8 Load MBCS Typedef
49 // --------- -------------- ---------- --------- ---------------
51 // char No Yes Yes *1 CSimpleIniA
52 // char Yes Yes Yes CSimpleIniCaseA
53 // wchar_t No Yes Yes CSimpleIniW
54 // wchar_t Yes Yes Yes CSimpleIniCaseW
56 // char No No *2 Yes CSimpleIniA
57 // char Yes Yes Yes CSimpleIniCaseA
58 // wchar_t No Yes Yes CSimpleIniW
59 // wchar_t Yes Yes Yes CSimpleIniCaseW
61 // char No Yes Yes CSimpleIniA
62 // char Yes Yes Yes CSimpleIniCaseA
63 // UChar No Yes Yes CSimpleIniW
64 // UChar Yes Yes Yes CSimpleIniCaseW
66 // *1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.
67 // *2 Only affects Windows. On Windows this uses MBCS functions and
68 // so may fold case incorrectly leading to uncertain results.
70 // 2. Call LoadFile() to load and parse the INI configuration file
72 // 3. Access and modify the data of the file using the following functions
74 // GetAllSections Return all section names
75 // GetAllKeys Return all key names for a section
76 // GetSection Return all key names and values in a section
77 // GetSectionSize Return the number of keys in a section
78 // GetValue Return a value for a section & key
79 // GetAllValues Return all values for a section & key
80 // SetValue Add or update a value for a section & key
81 // Delete Remove a section, or a key from a section
83 // 4. Call Save(), SaveFile() or SaveString() to save the INI configuration
84 // data (as necessary)
88 // Values that span multiple lines are created using the following format.
91 // .... multiline value ....
94 // Note the following:
95 // * The text used for ENDTAG can be anything and is used to find
96 // where the multi-line text ends.
97 // * The newline after ENDTAG in the start tag, and the newline
98 // before ENDTAG in the end tag is not included in the data value.
99 // * The ending tag must be on it's own line with no whitespace before
101 // * The multi-line value is not modified at load or save. This means
102 // that the newline format (PC, Unix, Mac) is whatever the original
107 // * To compile using Microsoft Visual Studio 6 or Embedded Visual C++ 4,
108 // you need to modify this header file and remove all instances of the
109 // "typename" keyword where error C2899 occurs.
110 // * To load UTF-8 data on Windows 95, you need to use Microsoft Layer for
111 // Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU.
112 // * When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked.
113 // * When using SI_CONVERT_ICU, ICU header files must be on the include
114 // path and icuuc.lib must be linked in.
115 // * To load a UTF-8 file on Windows AND expose it with SI_CHAR == char,
116 // you should use SI_CONVERT_GENERIC.
117 // * The collation (sorting) order used for sections and keys returned from
118 // iterators is NOT DEFINED. If collation order of the text is important
119 // then it should be done yourself by either supplying a replacement
120 // SI_STRLESS class, or by sorting the strings external to this library.
121 // * Usage of the <mbstring.h> header on Windows can be disabled by defining
122 // SI_NO_MBCS. This is defined automatically on Windows CE platforms.
126 // The licence text below is the boilerplate "MIT Licence" used from:
127 // http://www.opensource.org/licenses/mit-license.php
129 // Copyright (c) 2006, Brodie Thiesfield
131 // Permission is hereby granted, free of charge, to any person obtaining a copy
132 // of this software and associated documentation files (the "Software"), to deal
133 // in the Software without restriction, including without limitation the rights
134 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
135 // copies of the Software, and to permit persons to whom the Software is furnished
136 // to do so, subject to the following conditions:
138 // The above copyright notice and this permission notice shall be included in
139 // all copies or substantial portions of the Software.
141 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
142 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
143 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
144 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
145 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
146 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
148 #ifndef INCLUDED_SimpleIni_h
149 #define INCLUDED_SimpleIni_h
151 // Disable these warnings in MSVC:
152 // 4127 "conditional expression is constant" as the conversion classes trigger
153 // it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
154 // be optimized away in a release build.
155 // 4702 "unreachable code" as the MS STL header causes it in release mode.
156 // Again, the code causing the warning will be cleaned up by the compiler.
158 # pragma warning (disable: 4127 4702)
167 SI_OK
= 0, //!< No error
168 SI_UPDATED
= 1, //!< An existing value was updated
169 SI_INSERTED
= 2, //!< A new value was inserted
171 // note: test for any error with (retval < 0)
172 SI_FAIL
= -1, //!< Generic failure
173 SI_NOMEM
= -2, //!< Out of memory error
174 SI_FILE
= -3, //!< File error (see errno for detail error)
177 #define SI_BOM_UTF8 "\xEF\xBB\xBF"
180 # define SI_NEWLINE_A "\r\n"
181 # define SI_NEWLINE_W L"\r\n"
183 # define SI_NEWLINE_A "\n"
184 # define SI_NEWLINE_W L"\n"
187 #if defined(_WIN32) || defined(SI_CONVERT_ICU)
188 # define SI_HAS_WIDE_LOADFILE
191 #if defined(SI_CONVERT_ICU)
192 # include <unicode/ustring.h>
195 // ---------------------------------------------------------------------------
196 // MAIN TEMPLATE CLASS
197 // ---------------------------------------------------------------------------
200 * Simple INI file reader. This can be instantiated with the choice of unicode
201 * or native characterset, and case sensitive or insensitive comparisons of
202 * section and key names. The supported combinations are pre-defined with the
203 * following typedefs:
205 * Interface Case-sensitive Typedef
206 * --------- -------------- ---------------
207 * char No CSimpleIniA
208 * char Yes CSimpleIniCaseA
209 * wchar_t No CSimpleIniW
210 * wchar_t Yes CSimpleIniCaseW
212 * Note that using other types for the SI_CHAR is supported. For instance,
213 * unsigned char, unsigned short, etc. Note that where the alternative type
214 * is a different size to char/wchar_t you may need to supply new helper
215 * classes for SI_STRLESS and SI_CONVERTER.
217 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
218 class CSimpleIniTempl
221 /** map keys to values */
222 typedef std::multimap
<const SI_CHAR
*,const SI_CHAR
*,SI_STRLESS
> TKeyVal
;
224 /** map sections to key/value map */
225 typedef std::map
<const SI_CHAR
*,TKeyVal
,SI_STRLESS
> TSection
;
227 /** set of dependent string pointers. Note that these pointers are
228 * dependent on memory owned by CSimpleIni. */
229 typedef std::list
<const SI_CHAR
*> TNamesDepend
;
231 /** interface definition for the OutputWriter object to pass to Save()
232 * in order to output the INI file data. */
236 virtual ~OutputWriter() { }
237 virtual void Write(const char * a_pBuf
) = 0;
240 /** OutputWriter class to write the INI data to a file */
241 class FileWriter
: public OutputWriter
{
244 FileWriter(FILE * a_file
) : m_file(a_file
) { }
245 void Write(const char * a_pBuf
) {
246 fputs(a_pBuf
, m_file
);
250 /** OutputWriter class to write the INI data to a string */
251 class StringWriter
: public OutputWriter
{
252 std::string
& m_string
;
254 StringWriter(std::string
& a_string
) : m_string(a_string
) { }
255 void Write(const char * a_pBuf
) {
256 m_string
.append(a_pBuf
);
260 /** Characterset conversion utility class to convert strings to the
261 * same format as is used for the storage. */
262 class Converter
: private SI_CONVERTER
{
264 Converter(bool a_bStoreIsUtf8
) : SI_CONVERTER(a_bStoreIsUtf8
) {
265 m_scratch
.resize(1024);
267 Converter(const Converter
& rhs
) { operator=(rhs
); }
268 Converter
& operator=(const Converter
& rhs
) {
269 m_scratch
= rhs
.m_scratch
;
272 bool ConvertToStore(const SI_CHAR
* a_pszString
) {
273 size_t uLen
= SizeToStore(a_pszString
);
274 if (uLen
== (size_t)(-1)) {
277 while (uLen
> m_scratch
.size()) {
278 m_scratch
.resize(m_scratch
.size() * 2);
280 return SI_CONVERTER::ConvertToStore(
282 const_cast<char*>(m_scratch
.data()),
285 const char * Data() { return m_scratch
.data(); }
287 std::string m_scratch
;
292 * Default constructor.
294 * @param a_bIsUtf8 See the method SetUnicode() for details.
295 * @param a_bMultiKey See the method SetMultiKey() for details.
296 * @param a_bMultiLine See the method SetMultiLine() for details.
298 CSimpleIniTempl(bool a_bIsUtf8
= false, bool a_bMultiKey
= false, bool a_bMultiLine
= false);
306 * Deallocate all memory stored by this object
311 * Set the storage format of the INI data. This affects both the loading
312 * and saving of the INI data using all of the Load/Save API functions.
313 * This value cannot be changed after any INI data has been loaded.
315 * If the file is not set to Unicode (UTF-8), then the data encoding is
316 * assumed to be the OS native encoding. This encoding is the system
317 * locale on Linux/Unix and the legacy MBCS encoding on Windows NT/2K/XP.
318 * If the storage format is set to Unicode then the file will be loaded
319 * as UTF-8 encoded data regardless of the native file encoding. If
320 * SI_CHAR == char then all of the char* parameters take and return UTF-8
321 * encoded data regardless of the system locale.
323 void SetUnicode(bool a_bIsUtf8
= true) {
324 if (!m_pData
) m_bStoreIsUtf8
= a_bIsUtf8
;
328 * Get the storage format of the INI data.
330 bool IsUnicode() const { return m_bStoreIsUtf8
; }
333 * Should multiple identical keys be permitted in the file. If set to false
334 * then the last value encountered will be used as the value of the key.
335 * If set to true, then all values will be available to be queried. For
336 * example, with the following input:
342 * Then with SetMultiKey(true), both of the values "value1" and "value2"
343 * will be returned for the key test. If SetMultiKey(false) is used, then
344 * the value for "test" will only be "value2". This value may be changed
347 void SetMultiKey(bool a_bAllowMultiKey
= true) {
348 m_bAllowMultiKey
= a_bAllowMultiKey
;
352 * Get the storage format of the INI data.
354 bool IsMultiKey() const { return m_bAllowMultiKey
; }
357 * Should data values be permitted to span multiple lines in the file. If
358 * set to false then the multi-line construct <<<TAG as a value will be
359 * returned as is instead of loading the data. This value may be changed
362 void SetMultiLine(bool a_bAllowMultiLine
= true) {
363 m_bAllowMultiLine
= a_bAllowMultiLine
;
367 * Query the status of multi-line data.
369 bool IsMultiLine() const { return m_bAllowMultiLine
; }
372 * Load an INI file from disk into memory
374 * @param a_pszFile Path of the file to be loaded. This will be passed
375 * to fopen() and so must be a valid path for the
378 * @return SI_Error See error definitions
380 SI_Error
LoadFile(const char * a_pszFile
);
382 #ifdef SI_HAS_WIDE_LOADFILE
384 * Load an INI file from disk into memory
386 * @param a_pwszFile Path of the file to be loaded in UTF-16. This will
387 * be passed to _wfopen() on Windows. There is no
388 * wchar_t fopen function on Linux/Unix so this
389 * function is not supported there.
391 * @return SI_Error See error definitions
393 SI_Error
LoadFile(const wchar_t * a_pwszFile
);
397 * Load INI file data direct from memory
399 * @param a_pData Data to be loaded
400 * @param a_uDataLen Length of the data in bytes
402 * @return SI_Error See error definitions
405 const char * a_pData
,
409 * Save the INI data. The data will be written to the output device
410 * in a format appropriate to the current data, selected by:
414 * char same format as when loaded (MBCS or UTF-8)
418 * Note that comments, etc from the original data are not saved. Only the
419 * valid data contents stored in the file are written out. Any data
420 * prepended or appended to the output device should use the same format
421 * (MBCS or UTF-8) as this data, use the GetConverter() method to convert
422 * text to the correct format.
424 * To add a BOM to UTF-8 data, write it out manually at the very beginning
425 * like is done in SaveFile when a_bUseBOM is true.
427 SI_Error
Save(OutputWriter
& a_oOutput
) const;
430 * Save the INI data to a file. See Save() for details. Do not set
431 * a_bUseBOM to true if any information has been written to the file
432 * prior to calling this method.
434 * @param a_pFile Handle to a file. File should be opened for
436 * @param a_bUseBOM Prepend the UTF-8 BOM if the output stream is
439 SI_Error
SaveFile(FILE * a_pFile
, bool a_bUseBOM
= false) const {
440 FileWriter
writer(a_pFile
);
441 if (m_bStoreIsUtf8
&& a_bUseBOM
) {
442 writer
.Write(SI_BOM_UTF8
);
448 * Save the INI data to a string. See Save() for details.
450 * @param a_sBuffer String to have the INI data appended to.
452 SI_Error
SaveString(std::string
& a_sBuffer
) const {
453 StringWriter
writer(a_sBuffer
);
458 * Retrieve the value for a specific key. If multiple keys are enabled
459 * (see SetMultiKey) then only the first value associated with that key
460 * will be returned, see GetAllValues for getting all values with multikey.
462 * NOTE! The returned value is a pointer to string data stored in memory
463 * owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
464 * or Reset while you are using this pointer!
466 * @param a_pSection Section to search
467 * @param a_pKey Key to search for
468 * @param a_pDefault Value to return if the key is not found
469 * @param a_pHasMultiple Optionally receive notification of if there are
470 * multiple entries for this key.
472 * @return a_pDefault Key was not found in the section
473 * @return other Value of the key
475 const SI_CHAR
* GetValue(
476 const SI_CHAR
* a_pSection
,
477 const SI_CHAR
* a_pKey
,
478 const SI_CHAR
* a_pDefault
= 0,
479 bool * a_pHasMultiple
= 0) const;
482 * Retrieve all values for a specific key. This method can be used when
483 * multiple keys are both enabled and disabled.
485 * NOTE! The returned values are pointers to string data stored in memory
486 * owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
487 * or Reset while you are using this pointer!
489 * @param a_pSection Section to search
490 * @param a_pKey Key to search for
491 * @param a_values List to return if the key is not found
492 * @param a_pHasMultiple Optionally receive notification of if there are
493 * multiple entries for this key.
495 * @return a_pDefault Key was not found in the section
496 * @return other Value of the key
499 const SI_CHAR
* a_pSection
,
500 const SI_CHAR
* a_pKey
,
501 TNamesDepend
& a_values
) const;
504 * Add or update a section or value. This will always insert
505 * when multiple keys are enabled.
507 * @param a_pSection Section to add or update
508 * @param a_pKey Key to add or update. Set to NULL to
509 * create an empty section.
510 * @param a_pValue Value to set. Set to NULL to create an
513 * @return SI_Error See error definitions
514 * @return SI_UPDATED Value was updated
515 * @return SI_INSERTED Value was inserted
518 const SI_CHAR
* a_pSection
,
519 const SI_CHAR
* a_pKey
,
520 const SI_CHAR
* a_pValue
)
522 return AddEntry(a_pSection
, a_pKey
, a_pValue
, true);
526 * Delete an entire section, or a key from a section. Note that the
527 * data returned by GetSection is invalid and must not be used after
528 * anything has been deleted from that section using this method.
529 * Note when multiple keys is enabled, this will delete all keys with
530 * that name; there is no way to selectively delete individual key/values
533 * @param a_pSection Section to delete key from, or if
534 * a_pKey is NULL, the section to remove.
535 * @param a_pKey Key to remove from the section. Set to
536 * NULL to remove the entire section.
537 * @param a_bRemoveEmpty If the section is empty after this key has
538 * been deleted, should the empty section be
541 * @return true Key or section was deleted.
542 * @return false Key or section was not found.
545 const SI_CHAR
* a_pSection
,
546 const SI_CHAR
* a_pKey
,
547 bool a_bRemoveEmpty
= false);
550 * Query the number of keys in a specific section. Note that if multiple
551 * keys are enabled, then this value may be different to the number of
552 * keys returned by GetAllKeys.
554 * @param a_pSection Section to request data for
556 * @return -1 Section does not exist in the file
557 * @return >=0 Number of keys in the section
560 const SI_CHAR
* a_pSection
) const;
563 * Retrieve all key and value pairs for a section. The data is returned
564 * as a pointer to an STL map and can be iterated or searched as
565 * desired. Note that multiple entries for the same key may exist when
566 * multiple keys have been enabled.
568 * NOTE! This structure contains only pointers to strings. The actual
569 * string data is stored in memory owned by CSimpleIni. Ensure that the
570 * CSimpleIni object is not destroyed or Reset() while these strings
573 * @param a_pSection Name of the section to return
574 * @param a_pData Pointer to the section data.
575 * @return boolean Was a section matching the supplied
578 const TKeyVal
* GetSection(
579 const SI_CHAR
* a_pSection
) const;
582 * Retrieve all section names. The list is returned as an STL vector of
583 * names and can be iterated or searched as necessary. Note that the
584 * collation order of the returned strings is NOT DEFINED.
586 * NOTE! This structure contains only pointers to strings. The actual
587 * string data is stored in memory owned by CSimpleIni. Ensure that the
588 * CSimpleIni object is not destroyed or Reset() while these strings
591 * @param a_names Vector that will receive all of the section
592 * names. See note above!
595 TNamesDepend
& a_names
) const;
598 * Retrieve all unique key names in a section. The collation order of the
599 * returned strings is NOT DEFINED. Only unique key names are returned.
601 * NOTE! This structure contains only pointers to strings. The actual
602 * string data is stored in memory owned by CSimpleIni. Ensure that the
603 * CSimpleIni object is not destroyed or Reset() while these strings
606 * @param a_pSection Section to request data for
607 * @param a_names List that will receive all of the key
608 * names. See note above!
611 const SI_CHAR
* a_pSection
,
612 TNamesDepend
& a_names
) const;
615 * Return a conversion object to convert text to the same encoding
616 * as is used by the Save(), SaveFile() and SaveString() functions.
617 * Use this to prepare the strings that you wish to append or prepend
618 * to the output INI data.
620 Converter
GetConverter() const {
621 return Converter(m_bStoreIsUtf8
);
625 /** Load the file from a file pointer. */
626 SI_Error
LoadFile(FILE * a_fpFile
);
628 /** Parse the data looking for the next valid entry. The memory pointed to
629 * by a_pData is modified by inserting NULL characters. The pointer is
630 * updated to the current location in the block of text. */
633 const SI_CHAR
*& a_pSection
,
634 const SI_CHAR
*& a_pKey
,
635 const SI_CHAR
*& a_pVal
) const;
637 /** Add the section/key/value to our data. Strings will be copied only
638 * if a_bCopyStrings is set to true, otherwise the pointers will be
641 const SI_CHAR
* a_pSection
,
642 const SI_CHAR
* a_pKey
,
643 const SI_CHAR
* a_pValue
,
644 bool a_bCopyStrings
);
646 /** Is the supplied character a whitespace character? */
647 inline bool IsSpace(SI_CHAR ch
) const {
648 return (ch
== ' ' || ch
== '\t' || ch
== '\r' || ch
== '\n');
651 /** Does the supplied character start a comment line? */
652 inline bool IsComment(SI_CHAR ch
) const {
653 return (ch
== ';' || ch
== '#');
656 /** Make a copy of the supplied string, replacing the original pointer. */
657 SI_Error
CopyString(const SI_CHAR
*& a_pString
);
659 /** Delete a string from the copied strings buffer if necessary. */
660 void DeleteString(const SI_CHAR
* a_pString
);
662 /** Internal use of our string comparison function */
663 bool IsEqual(const SI_CHAR
* a_pLeft
, const SI_CHAR
* a_pRight
) const {
664 static const SI_STRLESS strless
= SI_STRLESS();
665 return !strless(a_pLeft
, a_pRight
) && !strless(a_pRight
, a_pLeft
);
668 bool IsMultiLineTag(const SI_CHAR
* a_pData
) const;
669 bool IsMultiLineData(const SI_CHAR
* a_pData
) const;
670 bool FindMultiLine(SI_CHAR
*& a_pData
, const SI_CHAR
*& a_pVal
) const;
671 bool IsNewLineChar(SI_CHAR a_c
) const;
674 /** Copy of the INI file data in our character format. This will be
675 * modified when parsed to have NULL characters added after all
676 * interesting string entries. All of the string pointers to sections,
677 * keys and values point into this block of memory.
681 /** Length of the data that we have stored. Used when deleting strings
682 * to determine if the string is stored here or in the allocated string
687 /** Parsed INI data. Section -> (Key -> Value). */
690 /** This vector stores allocated memory for copies of strings that have
691 * been supplied after the file load. It will be empty unless SetValue()
694 TNamesDepend m_strings
;
696 /** Is the format of our datafile UTF-8 or MBCS? */
699 /** Are multiple values permitted for the same key? */
700 bool m_bAllowMultiKey
;
702 /** Are data values permitted to span multiple lines? */
703 bool m_bAllowMultiLine
;
706 // ---------------------------------------------------------------------------
708 // ---------------------------------------------------------------------------
710 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
711 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::CSimpleIniTempl(
712 bool a_bIsUtf8
, bool a_bAllowMultiKey
, bool a_bAllowMultiLine
)
715 m_bStoreIsUtf8(a_bIsUtf8
),
716 m_bAllowMultiKey(a_bAllowMultiKey
),
717 m_bAllowMultiLine(a_bAllowMultiLine
)
720 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
721 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::~CSimpleIniTempl()
726 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
728 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::Reset()
734 if (!m_data
.empty()) {
735 m_data
.erase(m_data
.begin(), m_data
.end());
738 // remove all strings
739 if (!m_strings
.empty()) {
740 typename
TNamesDepend::iterator i
= m_strings
.begin();
741 for (; i
!= m_strings
.end(); ++i
) {
742 delete[] const_cast<SI_CHAR
*>(*i
);
744 m_strings
.erase(m_strings
.begin(), m_strings
.end());
748 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
750 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::LoadFile(
751 const char * a_pszFile
)
753 FILE * fp
= fopen(a_pszFile
, "rb");
757 SI_Error rc
= LoadFile(fp
);
762 #ifdef SI_HAS_WIDE_LOADFILE
763 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
765 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::LoadFile(
766 const wchar_t * a_pwszFile
)
770 errno_t err
= _wfopen_s(&fp
, a_pwszFile
, L
"rb");
771 if ((err
!=0)||(!fp
)) {
774 SI_Error rc
= LoadFile(fp
);
777 #else // SI_CONVERT_ICU
779 u_austrncpy(szFile
, a_pwszFile
, sizeof(szFile
));
780 return LoadFile(szFile
);
785 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
787 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::LoadFile(
790 // load the raw file data
791 int retval
= fseek(a_fpFile
, 0, SEEK_END
);
795 long lSize
= ftell(a_fpFile
);
799 char * pData
= new char[lSize
];
803 fseek(a_fpFile
, 0, SEEK_SET
);
804 size_t uRead
= fread(pData
, sizeof(char), lSize
, a_fpFile
);
805 if (uRead
!= (size_t) lSize
) {
810 // convert the raw data to unicode
811 SI_Error rc
= LoadFile(pData
, uRead
);
816 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
818 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::LoadFile(
819 const char * a_pData
,
822 SI_CONVERTER
converter(m_bStoreIsUtf8
);
824 // consume the UTF-8 BOM if it exists
825 if (m_bStoreIsUtf8
&& a_uDataLen
>= 3) {
826 if (memcmp(a_pData
, SI_BOM_UTF8
, 3) == 0) {
832 // determine the length of the converted data
833 size_t uLen
= converter
.SizeFromStore(a_pData
, a_uDataLen
);
834 if (uLen
== (size_t)(-1)) {
838 // allocate memory for the data, ensure that there is a NULL
839 // terminator wherever the converted data ends
840 SI_CHAR
* pData
= new SI_CHAR
[uLen
+1];
844 memset(pData
, 0, sizeof(SI_CHAR
)*(uLen
+1));
847 if (!converter
.ConvertFromStore(a_pData
, a_uDataLen
, pData
, uLen
)) {
853 const static SI_CHAR empty
= 0;
854 SI_CHAR
* pWork
= pData
;
855 const SI_CHAR
* pSection
= &empty
;
856 const SI_CHAR
* pKey
= 0;
857 const SI_CHAR
* pVal
= 0;
859 // add every entry in the file to the data table. We copy the strings if
860 // we are loading data into this class when we already have stored some
861 // because we only store a single block.
863 bool bCopyStrings
= (m_pData
!= 0);
864 while (FindEntry(pWork
, pSection
, pKey
, pVal
)) {
865 rc
= AddEntry(pSection
, pKey
, pVal
, bCopyStrings
);
866 if (rc
< 0) return rc
;
869 // store these strings if we didn't copy them
881 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
883 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::FindEntry(
885 const SI_CHAR
*& a_pSection
,
886 const SI_CHAR
*& a_pKey
,
887 const SI_CHAR
*& a_pVal
) const
891 // skip spaces and empty lines
892 while (*a_pData
&& IsSpace(*a_pData
)) {
899 // skip comment lines
900 if (IsComment(*a_pData
)) {
901 while (*a_pData
&& !IsNewLineChar(*a_pData
)) {
907 // process section names
908 if (*a_pData
== '[') {
909 // skip leading spaces
911 while (*a_pData
&& IsSpace(*a_pData
)) {
915 // find the end of the section name (it may contain spaces)
916 // and convert it to lowercase as necessary
917 a_pSection
= a_pData
;
918 while (*a_pData
&& *a_pData
!= ']' && !IsNewLineChar(*a_pData
)) {
922 // if it's an invalid line, just skip it
923 if (*a_pData
!= ']') {
927 // remove trailing spaces from the section
928 pTrail
= a_pData
- 1;
929 while (pTrail
>= a_pSection
&& IsSpace(*pTrail
)) {
935 // skip to the end of the line
936 ++a_pData
; // safe as checked that it == ']' above
937 while (*a_pData
&& !IsNewLineChar(*a_pData
)) {
946 // find the end of the key name (it may contain spaces)
947 // and convert it to lowercase as necessary
949 while (*a_pData
&& *a_pData
!= '=' && !IsNewLineChar(*a_pData
)) {
953 // if it's an invalid line, just skip it
954 if (*a_pData
!= '=') {
958 // empty keys are invalid
959 if (a_pKey
== a_pData
) {
960 while (*a_pData
&& !IsNewLineChar(*a_pData
)) {
966 // remove trailing spaces from the key
967 pTrail
= a_pData
- 1;
968 while (pTrail
>= a_pKey
&& IsSpace(*pTrail
)) {
974 // skip leading whitespace on the value
975 ++a_pData
; // safe as checked that it == '=' above
976 while (*a_pData
&& !IsNewLineChar(*a_pData
) && IsSpace(*a_pData
)) {
980 // find the end of the value which is the end of this line
982 while (*a_pData
&& !IsNewLineChar(*a_pData
)) {
986 // remove trailing spaces from the value
987 pTrail
= a_pData
- 1;
988 if (*a_pData
) { // prepare for the next round
991 while (pTrail
>= a_pVal
&& IsSpace(*pTrail
)) {
997 // check for multi-line entries
998 if (m_bAllowMultiLine
&& IsMultiLineTag(a_pVal
)) {
999 return FindMultiLine(a_pData
, a_pVal
);
1002 // return the standard entry
1009 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1011 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::IsMultiLineTag(
1012 const SI_CHAR
* a_pVal
1015 // check for the "<<<" prefix for a multi-line entry
1016 if (*a_pVal
++ != '<') return false;
1017 if (*a_pVal
++ != '<') return false;
1018 if (*a_pVal
++ != '<') return false;
1022 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1024 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::IsMultiLineData(
1025 const SI_CHAR
* a_pData
1028 // data is multi-line if it has any of the following features:
1029 // * whitespace prefix
1030 // * embedded newlines
1031 // * whitespace suffix
1039 if (IsSpace(*a_pData
)) {
1043 // embedded newlines
1045 if (IsNewLineChar(*a_pData
)) {
1051 if (IsSpace(*--a_pData
)) {
1058 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1060 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::IsNewLineChar(
1063 return a_c
== '\n' || a_c
== '\r';
1066 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1068 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::FindMultiLine(
1070 const SI_CHAR
*& a_pVal
1073 // skip the "<<<" to get the tag that will end the multiline
1074 const SI_CHAR
* pTagName
= a_pVal
+ 3;
1075 a_pVal
= a_pData
; // real value starts on next line
1077 // find the end tag. This tag must start in column 1 and be
1078 // followed by a newline. No whitespace removal is done while
1079 // searching for this tag.
1081 SI_CHAR cRememberThis
;
1083 // find the beginning and end of this line
1084 while (IsNewLineChar(*a_pData
)) ++a_pData
;
1086 while (*a_pData
&& !IsNewLineChar(*a_pData
)) ++a_pData
;
1088 // end the line with a NULL
1089 cRememberThis
= *a_pData
;
1092 // see if we have found the tag
1093 if (IsEqual(pLine
, pTagName
)) {
1094 // null terminate the data before the newline of the previous line.
1095 // If you want a new line at the end of the line then add an empty
1096 // line before the tag.
1098 if (*(pLine
-1) == '\r') {
1099 // handle Windows style newlines. This handles Unix newline files
1100 // on Windows and Windows style newlines on Unix. \n\r
1105 if (cRememberThis
) {
1111 // otherwise put the char back and continue checking
1112 if (!cRememberThis
) {
1115 *a_pData
++ = cRememberThis
;
1119 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1121 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::CopyString(
1122 const SI_CHAR
*& a_pString
)
1125 if (sizeof(SI_CHAR
) == sizeof(char)) {
1126 uLen
= strlen((const char *)a_pString
);
1128 else if (sizeof(SI_CHAR
) == sizeof(wchar_t)) {
1129 uLen
= wcslen((const wchar_t *)a_pString
);
1132 for ( ; a_pString
[uLen
]; ++uLen
) /*loop*/ ;
1134 ++uLen
; // NULL character
1135 SI_CHAR
* pCopy
= new SI_CHAR
[uLen
];
1139 memcpy(pCopy
, a_pString
, sizeof(SI_CHAR
)*uLen
);
1140 m_strings
.push_back(pCopy
);
1145 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1147 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::AddEntry(
1148 const SI_CHAR
* a_pSection
,
1149 const SI_CHAR
* a_pKey
,
1150 const SI_CHAR
* a_pValue
,
1151 bool a_bCopyStrings
)
1154 bool bInserted
= false;
1156 // check for existence of the section first if we need string copies
1157 typename
TSection::iterator iSection
= m_data
.end();
1158 if (a_bCopyStrings
) {
1159 iSection
= m_data
.find(a_pSection
);
1160 if (iSection
== m_data
.end()) {
1161 // if the section doesn't exist then we need a copy as the
1162 // string needs to last beyond the end of this function
1163 // because we will be inserting the section next
1164 rc
= CopyString(a_pSection
);
1165 if (rc
< 0) return rc
;
1169 // create the section entry
1170 if (iSection
== m_data
.end()) {
1171 std::pair
<typename
TSection::iterator
,bool> i
=
1173 typename
TSection::value_type( a_pSection
, TKeyVal() ) );
1177 if (!a_pKey
|| !a_pValue
) {
1178 // section only entries are specified with pKey and pVal as NULL
1179 return bInserted
? SI_INSERTED
: SI_UPDATED
;
1182 // check for existence of the key
1183 TKeyVal
& keyval
= iSection
->second
;
1184 typename
TKeyVal::iterator iKey
= keyval
.find(a_pKey
);
1186 // make string copies if necessary
1187 if (a_bCopyStrings
) {
1188 if (m_bAllowMultiKey
|| iKey
== keyval
.end()) {
1189 // if the key doesn't exist then we need a copy as the
1190 // string needs to last beyond the end of this function
1191 // because we will be inserting the key next
1192 rc
= CopyString(a_pKey
);
1193 if (rc
< 0) return rc
;
1196 // we always need a copy of the value
1197 rc
= CopyString(a_pValue
);
1198 if (rc
< 0) return rc
;
1201 // create the key entry
1202 if (iKey
== keyval
.end() || m_bAllowMultiKey
) {
1203 iKey
= keyval
.insert(typename
TKeyVal::value_type(a_pKey
, (const SI_CHAR
*)0));
1206 iKey
->second
= a_pValue
;
1207 return bInserted
? SI_INSERTED
: SI_UPDATED
;
1210 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1212 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetValue(
1213 const SI_CHAR
* a_pSection
,
1214 const SI_CHAR
* a_pKey
,
1215 const SI_CHAR
* a_pDefault
,
1216 bool * a_pHasMultiple
) const
1218 if (a_pHasMultiple
) {
1219 *a_pHasMultiple
= false;
1221 if (!a_pSection
|| !a_pKey
) {
1224 typename
TSection::const_iterator iSection
= m_data
.find(a_pSection
);
1225 if (iSection
== m_data
.end()) {
1228 typename
TKeyVal::const_iterator iKeyVal
= iSection
->second
.find(a_pKey
);
1229 if (iKeyVal
== iSection
->second
.end()) {
1233 // check for multiple entries with the same key
1234 if (m_bAllowMultiKey
&& a_pHasMultiple
) {
1235 typename
TKeyVal::const_iterator iTemp
= iKeyVal
;
1236 if (++iTemp
!= iSection
->second
.end()) {
1237 if (IsEqual(a_pKey
, iTemp
->first
)) {
1238 *a_pHasMultiple
= true;
1243 return iKeyVal
->second
;
1246 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1248 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetAllValues(
1249 const SI_CHAR
* a_pSection
,
1250 const SI_CHAR
* a_pKey
,
1251 TNamesDepend
& a_values
) const
1253 if (!a_pSection
|| !a_pKey
) {
1256 typename
TSection::const_iterator iSection
= m_data
.find(a_pSection
);
1257 if (iSection
== m_data
.end()) {
1260 typename
TKeyVal::const_iterator iKeyVal
= iSection
->second
.find(a_pKey
);
1261 if (iKeyVal
== iSection
->second
.end()) {
1265 // insert all values for this key
1266 a_values
.push_back(iKeyVal
->second
);
1267 if (m_bAllowMultiKey
) {
1269 while (iKeyVal
!= iSection
->second
.end() && IsEqual(a_pKey
, iKeyVal
->first
)) {
1270 a_values
.push_back(iKeyVal
->second
);
1277 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1279 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetSectionSize(
1280 const SI_CHAR
* a_pSection
) const
1286 typename
TSection::const_iterator iSection
= m_data
.find(a_pSection
);
1287 if (iSection
== m_data
.end()) {
1290 const TKeyVal
& section
= iSection
->second
;
1292 // if multi-key isn't permitted then the section size is
1293 // the number of keys that we have.
1294 if (!m_bAllowMultiKey
|| section
.empty()) {
1295 return (int) section
.size();
1298 // otherwise we need to count them
1300 const SI_CHAR
* pLastKey
= 0;
1301 typename
TKeyVal::const_iterator iKeyVal
= section
.begin();
1302 for (int n
= 0; iKeyVal
!= section
.end(); ++iKeyVal
, ++n
) {
1303 if (!pLastKey
|| !IsEqual(pLastKey
, iKeyVal
->first
)) {
1305 pLastKey
= iKeyVal
->first
;
1311 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1312 const typename CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::TKeyVal
*
1313 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetSection(
1314 const SI_CHAR
* a_pSection
) const
1317 typename
TSection::const_iterator i
= m_data
.find(a_pSection
);
1318 if (i
!= m_data
.end()) {
1319 return &(i
->second
);
1325 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1327 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetAllSections(
1328 TNamesDepend
& a_names
) const
1330 typename
TSection::const_iterator i
= m_data
.begin();
1331 for (int n
= 0; i
!= m_data
.end(); ++i
, ++n
) {
1332 a_names
.push_back(i
->first
);
1336 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1338 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetAllKeys(
1339 const SI_CHAR
* a_pSection
,
1340 TNamesDepend
& a_names
) const
1346 typename
TSection::const_iterator iSection
= m_data
.find(a_pSection
);
1347 if (iSection
== m_data
.end()) {
1351 const TKeyVal
& section
= iSection
->second
;
1352 const SI_CHAR
* pLastKey
= 0;
1353 typename
TKeyVal::const_iterator iKeyVal
= section
.begin();
1354 for (int n
= 0; iKeyVal
!= section
.end(); ++iKeyVal
, ++n
) {
1355 if (!pLastKey
|| !IsEqual(pLastKey
, iKeyVal
->first
)) {
1356 a_names
.push_back(iKeyVal
->first
);
1357 pLastKey
= iKeyVal
->first
;
1362 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1364 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::Save(
1365 OutputWriter
& a_oOutput
) const
1367 Converter
convert(m_bStoreIsUtf8
);
1369 // iterate through our sections and output the data
1370 bool bFirstLine
= true;
1371 typename
TSection::const_iterator iSection
= m_data
.begin();
1372 for ( ; iSection
!= m_data
.end(); ++iSection
) {
1373 // write the section (unless there is no section name)
1374 if (iSection
->first
[0]) {
1375 if (!convert
.ConvertToStore(iSection
->first
)) {
1379 a_oOutput
.Write(SI_NEWLINE_A
);
1381 a_oOutput
.Write("[");
1382 a_oOutput
.Write(convert
.Data());
1383 a_oOutput
.Write("]");
1384 a_oOutput
.Write(SI_NEWLINE_A
);
1387 // write all keys and values
1388 typename
TKeyVal::const_iterator iKeyVal
= iSection
->second
.begin();
1389 for ( ; iKeyVal
!= iSection
->second
.end(); ++iKeyVal
) {
1391 if (!convert
.ConvertToStore(iKeyVal
->first
)) {
1394 a_oOutput
.Write(convert
.Data());
1397 if (!convert
.ConvertToStore(iKeyVal
->second
)) {
1400 a_oOutput
.Write("=");
1401 if (m_bAllowMultiLine
&& IsMultiLineData(iKeyVal
->second
)) {
1402 a_oOutput
.Write("<<<SI-END-OF-MULTILINE-TEXT" SI_NEWLINE_A
);
1403 a_oOutput
.Write(convert
.Data());
1404 a_oOutput
.Write(SI_NEWLINE_A
"SI-END-OF-MULTILINE-TEXT");
1407 a_oOutput
.Write(convert
.Data());
1409 a_oOutput
.Write(SI_NEWLINE_A
);
1418 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1420 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::Delete(
1421 const SI_CHAR
* a_pSection
,
1422 const SI_CHAR
* a_pKey
,
1423 bool a_bRemoveEmpty
)
1429 typename
TSection::iterator iSection
= m_data
.find(a_pSection
);
1430 if (iSection
== m_data
.end()) {
1434 // remove a single key if we have a keyname
1436 typename
TKeyVal::iterator iKeyVal
= iSection
->second
.find(a_pKey
);
1437 if (iKeyVal
== iSection
->second
.end()) {
1441 // remove any copied strings and then the key
1442 typename
TKeyVal::iterator iDelete
;
1444 iDelete
= iKeyVal
++;
1446 DeleteString(iDelete
->first
);
1447 DeleteString(iDelete
->second
);
1448 iSection
->second
.erase(iDelete
);
1450 while (iKeyVal
!= iSection
->second
.end()
1451 && IsEqual(a_pKey
, iKeyVal
->first
));
1453 // done now if the section is not empty or we are not pruning away
1454 // the empty sections. Otherwise let it fall through into the section
1456 if (!a_bRemoveEmpty
|| !iSection
->second
.empty()) {
1461 // delete all copied strings from this section. The actual
1462 // entries will be removed when the section is removed.
1463 typename
TKeyVal::iterator iKeyVal
= iSection
->second
.begin();
1464 for ( ; iKeyVal
!= iSection
->second
.end(); ++iKeyVal
) {
1465 DeleteString(iKeyVal
->first
);
1466 DeleteString(iKeyVal
->second
);
1470 // delete the section itself
1471 DeleteString(iSection
->first
);
1472 m_data
.erase(iSection
);
1477 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1479 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::DeleteString(
1480 const SI_CHAR
* a_pString
)
1482 // strings may exist either inside the data block, or they will be
1483 // individually allocated and stored in m_strings. We only physically
1484 // delete those stored in m_strings.
1485 if (a_pString
< m_pData
|| a_pString
>= m_pData
+ m_uDataLen
) {
1486 typename
TNamesDepend::iterator i
= m_strings
.begin();
1487 for (;i
!= m_strings
.end(); ++i
) {
1488 if (a_pString
== *i
) {
1489 delete[] const_cast<SI_CHAR
*>(*i
);
1497 // ---------------------------------------------------------------------------
1498 // CONVERSION FUNCTIONS
1499 // ---------------------------------------------------------------------------
1501 // Defines the conversion classes for different libraries. Before including
1502 // SimpleIni.h, set the converter that you wish you use by defining one of the
1503 // following symbols.
1505 // SI_CONVERT_GENERIC Use the Unicode reference conversion library in
1506 // the accompanying files ConvertUTF.h/c
1507 // SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
1508 // ICU headers on include path and icuuc.lib
1509 // SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
1511 #if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
1513 # define SI_CONVERT_WIN32
1515 # define SI_CONVERT_GENERIC
1520 * Generic case-sensitive <less> comparison. This class returns numerically
1521 * ordered ASCII case-sensitive text for all possible sizes and types of
1524 template<class SI_CHAR
>
1525 struct SI_GenericCase
{
1526 bool operator()(const SI_CHAR
* pLeft
, const SI_CHAR
* pRight
) const {
1528 for ( ;*pLeft
&& *pRight
; ++pLeft
, ++pRight
) {
1529 cmp
= (long) *pLeft
- (long) *pRight
;
1534 return *pRight
!= 0;
1539 * Generic ASCII case-insensitive <less> comparison. This class returns
1540 * numerically ordered ASCII case-insensitive text for all possible sizes
1541 * and types of SI_CHAR. It is not safe for MBCS text comparison where
1542 * ASCII A-Z characters are used in the encoding of multi-byte characters.
1544 template<class SI_CHAR
>
1545 struct SI_GenericNoCase
{
1546 inline SI_CHAR
locase(SI_CHAR ch
) const {
1547 return (ch
< 'A' || ch
> 'Z') ? ch
: (ch
- 'A' + 'a');
1549 bool operator()(const SI_CHAR
* pLeft
, const SI_CHAR
* pRight
) const {
1551 for ( ;*pLeft
&& *pRight
; ++pLeft
, ++pRight
) {
1552 cmp
= (long) locase(*pLeft
) - (long) locase(*pRight
);
1557 return *pRight
!= 0;
1562 * Null conversion class for MBCS/UTF-8 to char (or equivalent).
1564 template<class SI_CHAR
>
1566 bool m_bStoreIsUtf8
;
1570 SI_ConvertA(bool a_bStoreIsUtf8
) : m_bStoreIsUtf8(a_bStoreIsUtf8
) { }
1572 /* copy and assignment */
1573 SI_ConvertA(const SI_ConvertA
& rhs
) { operator=(rhs
); }
1574 SI_ConvertA
& operator=(const SI_ConvertA
& rhs
) {
1575 m_bStoreIsUtf8
= rhs
.m_bStoreIsUtf8
;
1579 /** Calculate the number of SI_CHAR required for converting the input
1580 * from the storage format. The storage format is always UTF-8 or MBCS.
1582 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1583 * @param a_uInputDataLen Length of storage format data in bytes. This
1584 * must be the actual length of the data, including
1585 * NULL byte if NULL terminated string is required.
1586 * @return Number of SI_CHAR required by the string when
1587 * converted. If there are embedded NULL bytes in the
1588 * input data, only the string up and not including
1589 * the NULL byte will be converted.
1590 * @return -1 cast to size_t on a conversion error.
1592 size_t SizeFromStore(
1593 const char * /*a_pInputData*/,
1594 size_t a_uInputDataLen
)
1596 assert(a_uInputDataLen
!= (size_t) -1);
1598 // ASCII/MBCS/UTF-8 needs no conversion
1599 return a_uInputDataLen
;
1602 /** Convert the input string from the storage format to SI_CHAR.
1603 * The storage format is always UTF-8 or MBCS.
1605 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1606 * @param a_uInputDataLen Length of storage format data in bytes. This
1607 * must be the actual length of the data, including
1608 * NULL byte if NULL terminated string is required.
1609 * @param a_pOutputData Pointer to the output buffer to received the
1611 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
1612 * @return true if all of the input data was successfully
1615 bool ConvertFromStore(
1616 const char * a_pInputData
,
1617 size_t a_uInputDataLen
,
1618 SI_CHAR
* a_pOutputData
,
1619 size_t a_uOutputDataSize
)
1621 // ASCII/MBCS/UTF-8 needs no conversion
1622 if (a_uInputDataLen
> a_uOutputDataSize
) {
1625 memcpy(a_pOutputData
, a_pInputData
, a_uInputDataLen
);
1629 /** Calculate the number of char required by the storage format of this
1630 * data. The storage format is always UTF-8 or MBCS.
1632 * @param a_pInputData NULL terminated string to calculate the number of
1633 * bytes required to be converted to storage format.
1634 * @return Number of bytes required by the string when
1635 * converted to storage format. This size always
1636 * includes space for the terminating NULL character.
1637 * @return -1 cast to size_t on a conversion error.
1640 const SI_CHAR
* a_pInputData
)
1642 // ASCII/MBCS/UTF-8 needs no conversion
1643 return strlen((const char *)a_pInputData
) + 1;
1646 /** Convert the input string to the storage format of this data.
1647 * The storage format is always UTF-8 or MBCS.
1649 * @param a_pInputData NULL terminated source string to convert. All of
1650 * the data will be converted including the
1651 * terminating NULL character.
1652 * @param a_pOutputData Pointer to the buffer to receive the converted
1654 * @param a_pOutputDataSize Size of the output buffer in char.
1655 * @return true if all of the input data, including the
1656 * terminating NULL character was successfully
1659 bool ConvertToStore(
1660 const SI_CHAR
* a_pInputData
,
1661 char * a_pOutputData
,
1662 size_t a_uOutputDataSize
)
1664 // calc input string length (SI_CHAR type and size independent)
1665 size_t uInputLen
= strlen((const char *)a_pInputData
) + 1;
1666 if (uInputLen
> a_uOutputDataSize
) {
1670 // ascii/UTF-8 needs no conversion
1671 memcpy(a_pOutputData
, a_pInputData
, uInputLen
);
1677 // ---------------------------------------------------------------------------
1678 // SI_CONVERT_GENERIC
1679 // ---------------------------------------------------------------------------
1680 #ifdef SI_CONVERT_GENERIC
1682 #define SI_Case SI_GenericCase
1683 #define SI_NoCase SI_GenericNoCase
1686 #include "ConvertUTF.h"
1689 * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference
1690 * library functions. This can be used on all platforms.
1692 template<class SI_CHAR
>
1694 bool m_bStoreIsUtf8
;
1698 SI_ConvertW(bool a_bStoreIsUtf8
) : m_bStoreIsUtf8(a_bStoreIsUtf8
) { }
1700 /* copy and assignment */
1701 SI_ConvertW(const SI_ConvertW
& rhs
) { operator=(rhs
); }
1702 SI_ConvertW
& operator=(const SI_ConvertW
& rhs
) {
1703 m_bStoreIsUtf8
= rhs
.m_bStoreIsUtf8
;
1707 /** Calculate the number of SI_CHAR required for converting the input
1708 * from the storage format. The storage format is always UTF-8 or MBCS.
1710 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1711 * @param a_uInputDataLen Length of storage format data in bytes. This
1712 * must be the actual length of the data, including
1713 * NULL byte if NULL terminated string is required.
1714 * @return Number of SI_CHAR required by the string when
1715 * converted. If there are embedded NULL bytes in the
1716 * input data, only the string up and not including
1717 * the NULL byte will be converted.
1718 * @return -1 cast to size_t on a conversion error.
1720 size_t SizeFromStore(
1721 const char * a_pInputData
,
1722 size_t a_uInputDataLen
)
1724 assert(a_uInputDataLen
!= (size_t) -1);
1726 if (m_bStoreIsUtf8
) {
1727 // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
1728 // so we just return the same number of characters required as for
1730 return a_uInputDataLen
;
1733 return mbstowcs(NULL
, a_pInputData
, a_uInputDataLen
);
1737 /** Convert the input string from the storage format to SI_CHAR.
1738 * The storage format is always UTF-8 or MBCS.
1740 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1741 * @param a_uInputDataLen Length of storage format data in bytes. This
1742 * must be the actual length of the data, including
1743 * NULL byte if NULL terminated string is required.
1744 * @param a_pOutputData Pointer to the output buffer to received the
1746 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
1747 * @return true if all of the input data was successfully
1750 bool ConvertFromStore(
1751 const char * a_pInputData
,
1752 size_t a_uInputDataLen
,
1753 SI_CHAR
* a_pOutputData
,
1754 size_t a_uOutputDataSize
)
1756 if (m_bStoreIsUtf8
) {
1757 // This uses the Unicode reference implementation to do the
1758 // conversion from UTF-8 to wchar_t. The required files are
1759 // ConvertUTF.h and ConvertUTF.c which should be included in
1760 // the distribution but are publically available from unicode.org
1761 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
1762 ConversionResult retval
;
1763 const UTF8
* pUtf8
= (const UTF8
*) a_pInputData
;
1764 if (sizeof(wchar_t) == sizeof(UTF32
)) {
1765 UTF32
* pUtf32
= (UTF32
*) a_pOutputData
;
1766 retval
= ConvertUTF8toUTF32(
1767 &pUtf8
, pUtf8
+ a_uInputDataLen
,
1768 &pUtf32
, pUtf32
+ a_uOutputDataSize
,
1771 else if (sizeof(wchar_t) == sizeof(UTF16
)) {
1772 UTF16
* pUtf16
= (UTF16
*) a_pOutputData
;
1773 retval
= ConvertUTF8toUTF16(
1774 &pUtf8
, pUtf8
+ a_uInputDataLen
,
1775 &pUtf16
, pUtf16
+ a_uOutputDataSize
,
1778 return retval
== conversionOK
;
1781 size_t retval
= mbstowcs(a_pOutputData
,
1782 a_pInputData
, a_uOutputDataSize
);
1783 return retval
!= (size_t)(-1);
1787 /** Calculate the number of char required by the storage format of this
1788 * data. The storage format is always UTF-8 or MBCS.
1790 * @param a_pInputData NULL terminated string to calculate the number of
1791 * bytes required to be converted to storage format.
1792 * @return Number of bytes required by the string when
1793 * converted to storage format. This size always
1794 * includes space for the terminating NULL character.
1795 * @return -1 cast to size_t on a conversion error.
1798 const SI_CHAR
* a_pInputData
)
1800 if (m_bStoreIsUtf8
) {
1801 // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
1803 while (a_pInputData
[uLen
]) {
1806 return (6 * uLen
) + 1;
1809 size_t uLen
= wcstombs(NULL
, a_pInputData
, 0);
1810 if (uLen
== (size_t)(-1)) {
1813 return uLen
+ 1; // include NULL terminator
1817 /** Convert the input string to the storage format of this data.
1818 * The storage format is always UTF-8 or MBCS.
1820 * @param a_pInputData NULL terminated source string to convert. All of
1821 * the data will be converted including the
1822 * terminating NULL character.
1823 * @param a_pOutputData Pointer to the buffer to receive the converted
1825 * @param a_pOutputDataSize Size of the output buffer in char.
1826 * @return true if all of the input data, including the
1827 * terminating NULL character was successfully
1830 bool ConvertToStore(
1831 const SI_CHAR
* a_pInputData
,
1832 char * a_pOutputData
,
1833 size_t a_uOutputDataSize
)
1835 if (m_bStoreIsUtf8
) {
1836 // calc input string length (SI_CHAR type and size independent)
1837 size_t uInputLen
= 0;
1838 while (a_pInputData
[uInputLen
]) {
1841 ++uInputLen
; // include the NULL char
1843 // This uses the Unicode reference implementation to do the
1844 // conversion from wchar_t to UTF-8. The required files are
1845 // ConvertUTF.h and ConvertUTF.c which should be included in
1846 // the distribution but are publically available from unicode.org
1847 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
1848 ConversionResult retval
;
1849 UTF8
* pUtf8
= (UTF8
*) a_pOutputData
;
1850 if (sizeof(wchar_t) == sizeof(UTF32
)) {
1851 const UTF32
* pUtf32
= (const UTF32
*) a_pInputData
;
1852 retval
= ConvertUTF32toUTF8(
1853 &pUtf32
, pUtf32
+ uInputLen
+ 1,
1854 &pUtf8
, pUtf8
+ a_uOutputDataSize
,
1857 else if (sizeof(wchar_t) == sizeof(UTF16
)) {
1858 const UTF16
* pUtf16
= (const UTF16
*) a_pInputData
;
1859 retval
= ConvertUTF16toUTF8(
1860 &pUtf16
, pUtf16
+ uInputLen
+ 1,
1861 &pUtf8
, pUtf8
+ a_uOutputDataSize
,
1864 return retval
== conversionOK
;
1867 size_t retval
= wcstombs(a_pOutputData
,
1868 a_pInputData
, a_uOutputDataSize
);
1869 return retval
!= (size_t) -1;
1874 #endif // SI_CONVERT_GENERIC
1877 // ---------------------------------------------------------------------------
1879 // ---------------------------------------------------------------------------
1880 #ifdef SI_CONVERT_ICU
1882 #define SI_Case SI_GenericCase
1883 #define SI_NoCase SI_GenericNoCase
1885 #include <unicode/ucnv.h>
1888 * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms.
1890 template<class SI_CHAR
>
1892 const char * m_pEncoding
;
1893 UConverter
* m_pConverter
;
1895 SI_ConvertW() : m_pEncoding(NULL
), m_pConverter(NULL
) { }
1897 SI_ConvertW(bool a_bStoreIsUtf8
) : m_pConverter(NULL
) {
1898 m_pEncoding
= a_bStoreIsUtf8
? "UTF-8" : NULL
;
1901 /* copy and assignment */
1902 SI_ConvertW(const SI_ConvertW
& rhs
) { operator=(rhs
); }
1903 SI_ConvertW
& operator=(const SI_ConvertW
& rhs
) {
1904 m_pEncoding
= rhs
.m_pEncoding
;
1905 m_pConverter
= NULL
;
1908 ~SI_ConvertW() { if (m_pConverter
) ucnv_close(m_pConverter
); }
1910 /** Calculate the number of UChar required for converting the input
1911 * from the storage format. The storage format is always UTF-8 or MBCS.
1913 * @param a_pInputData Data in storage format to be converted to UChar.
1914 * @param a_uInputDataLen Length of storage format data in bytes. This
1915 * must be the actual length of the data, including
1916 * NULL byte if NULL terminated string is required.
1917 * @return Number of UChar required by the string when
1918 * converted. If there are embedded NULL bytes in the
1919 * input data, only the string up and not including
1920 * the NULL byte will be converted.
1921 * @return -1 cast to size_t on a conversion error.
1923 size_t SizeFromStore(
1924 const char * a_pInputData
,
1925 size_t a_uInputDataLen
)
1927 assert(a_uInputDataLen
!= (size_t) -1);
1931 if (!m_pConverter
) {
1932 nError
= U_ZERO_ERROR
;
1933 m_pConverter
= ucnv_open(m_pEncoding
, &nError
);
1934 if (U_FAILURE(nError
)) {
1939 nError
= U_ZERO_ERROR
;
1940 ucnv_resetToUnicode(m_pConverter
);
1941 int32_t nLen
= ucnv_toUChars(m_pConverter
, NULL
, 0,
1942 a_pInputData
, (int32_t) a_uInputDataLen
, &nError
);
1943 if (nError
!= U_BUFFER_OVERFLOW_ERROR
) {
1947 return (size_t) nLen
;
1950 /** Convert the input string from the storage format to UChar.
1951 * The storage format is always UTF-8 or MBCS.
1953 * @param a_pInputData Data in storage format to be converted to UChar.
1954 * @param a_uInputDataLen Length of storage format data in bytes. This
1955 * must be the actual length of the data, including
1956 * NULL byte if NULL terminated string is required.
1957 * @param a_pOutputData Pointer to the output buffer to received the
1959 * @param a_uOutputDataSize Size of the output buffer in UChar.
1960 * @return true if all of the input data was successfully
1963 bool ConvertFromStore(
1964 const char * a_pInputData
,
1965 size_t a_uInputDataLen
,
1966 UChar
* a_pOutputData
,
1967 size_t a_uOutputDataSize
)
1971 if (!m_pConverter
) {
1972 nError
= U_ZERO_ERROR
;
1973 m_pConverter
= ucnv_open(m_pEncoding
, &nError
);
1974 if (U_FAILURE(nError
)) {
1979 nError
= U_ZERO_ERROR
;
1980 ucnv_resetToUnicode(m_pConverter
);
1981 ucnv_toUChars(m_pConverter
,
1982 a_pOutputData
, (int32_t) a_uOutputDataSize
,
1983 a_pInputData
, (int32_t) a_uInputDataLen
, &nError
);
1984 if (U_FAILURE(nError
)) {
1991 /** Calculate the number of char required by the storage format of this
1992 * data. The storage format is always UTF-8 or MBCS.
1994 * @param a_pInputData NULL terminated string to calculate the number of
1995 * bytes required to be converted to storage format.
1996 * @return Number of bytes required by the string when
1997 * converted to storage format. This size always
1998 * includes space for the terminating NULL character.
1999 * @return -1 cast to size_t on a conversion error.
2002 const UChar
* a_pInputData
)
2006 if (!m_pConverter
) {
2007 nError
= U_ZERO_ERROR
;
2008 m_pConverter
= ucnv_open(m_pEncoding
, &nError
);
2009 if (U_FAILURE(nError
)) {
2014 nError
= U_ZERO_ERROR
;
2015 ucnv_resetFromUnicode(m_pConverter
);
2016 int32_t nLen
= ucnv_fromUChars(m_pConverter
, NULL
, 0,
2017 a_pInputData
, -1, &nError
);
2018 if (nError
!= U_BUFFER_OVERFLOW_ERROR
) {
2022 return (size_t) nLen
+ 1;
2025 /** Convert the input string to the storage format of this data.
2026 * The storage format is always UTF-8 or MBCS.
2028 * @param a_pInputData NULL terminated source string to convert. All of
2029 * the data will be converted including the
2030 * terminating NULL character.
2031 * @param a_pOutputData Pointer to the buffer to receive the converted
2033 * @param a_pOutputDataSize Size of the output buffer in char.
2034 * @return true if all of the input data, including the
2035 * terminating NULL character was successfully
2038 bool ConvertToStore(
2039 const UChar
* a_pInputData
,
2040 char * a_pOutputData
,
2041 size_t a_uOutputDataSize
)
2045 if (!m_pConverter
) {
2046 nError
= U_ZERO_ERROR
;
2047 m_pConverter
= ucnv_open(m_pEncoding
, &nError
);
2048 if (U_FAILURE(nError
)) {
2053 nError
= U_ZERO_ERROR
;
2054 ucnv_resetFromUnicode(m_pConverter
);
2055 ucnv_fromUChars(m_pConverter
,
2056 a_pOutputData
, (int32_t) a_uOutputDataSize
,
2057 a_pInputData
, -1, &nError
);
2058 if (U_FAILURE(nError
)) {
2066 #endif // SI_CONVERT_ICU
2069 // ---------------------------------------------------------------------------
2071 // ---------------------------------------------------------------------------
2072 #ifdef SI_CONVERT_WIN32
2074 #define SI_Case SI_GenericCase
2076 // Windows CE doesn't have errno or MBCS libraries
2083 #include <windows.h>
2085 # define SI_NoCase SI_GenericNoCase
2086 #else // !SI_NO_MBCS
2088 * Case-insensitive comparison class using Win32 MBCS functions. This class
2089 * returns a case-insensitive semi-collation order for MBCS text. It may not
2090 * be safe for UTF-8 text returned in char format as we don't know what
2091 * characters will be folded by the function! Therefore, if you are using
2092 * SI_CHAR == char and SetUnicode(true), then you need to use the generic
2093 * SI_NoCase class instead.
2095 #include <mbstring.h>
2096 template<class SI_CHAR
>
2098 bool operator()(const SI_CHAR
* pLeft
, const SI_CHAR
* pRight
) const {
2099 if (sizeof(SI_CHAR
) == sizeof(char)) {
2100 return _mbsicmp((const unsigned char *)pLeft
,
2101 (const unsigned char *)pRight
) < 0;
2103 if (sizeof(SI_CHAR
) == sizeof(wchar_t)) {
2104 return _wcsicmp((const wchar_t *)pLeft
,
2105 (const wchar_t *)pRight
) < 0;
2107 return SI_GenericNoCase
<SI_CHAR
>()(pLeft
, pRight
);
2110 #endif // SI_NO_MBCS
2113 * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses
2114 * only the Win32 functions and doesn't require the external Unicode UTF-8
2115 * conversion library. It will not work on Windows 95 without using Microsoft
2116 * Layer for Unicode in your application.
2118 template<class SI_CHAR
>
2124 SI_ConvertW(bool a_bStoreIsUtf8
) {
2125 m_uCodePage
= a_bStoreIsUtf8
? CP_UTF8
: CP_ACP
;
2128 /* copy and assignment */
2129 SI_ConvertW(const SI_ConvertW
& rhs
) { operator=(rhs
); }
2130 SI_ConvertW
& operator=(const SI_ConvertW
& rhs
) {
2131 m_uCodePage
= rhs
.m_uCodePage
;
2135 /** Calculate the number of SI_CHAR required for converting the input
2136 * from the storage format. The storage format is always UTF-8 or MBCS.
2138 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2139 * @param a_uInputDataLen Length of storage format data in bytes. This
2140 * must be the actual length of the data, including
2141 * NULL byte if NULL terminated string is required.
2142 * @return Number of SI_CHAR required by the string when
2143 * converted. If there are embedded NULL bytes in the
2144 * input data, only the string up and not including
2145 * the NULL byte will be converted.
2146 * @return -1 cast to size_t on a conversion error.
2148 size_t SizeFromStore(
2149 const char * a_pInputData
,
2150 size_t a_uInputDataLen
)
2152 assert(a_uInputDataLen
!= (size_t) -1);
2154 int retval
= MultiByteToWideChar(
2156 a_pInputData
, (int) a_uInputDataLen
,
2158 return (size_t)(retval
> 0 ? retval
: -1);
2161 /** Convert the input string from the storage format to SI_CHAR.
2162 * The storage format is always UTF-8 or MBCS.
2164 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2165 * @param a_uInputDataLen Length of storage format data in bytes. This
2166 * must be the actual length of the data, including
2167 * NULL byte if NULL terminated string is required.
2168 * @param a_pOutputData Pointer to the output buffer to received the
2170 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2171 * @return true if all of the input data was successfully
2174 bool ConvertFromStore(
2175 const char * a_pInputData
,
2176 size_t a_uInputDataLen
,
2177 SI_CHAR
* a_pOutputData
,
2178 size_t a_uOutputDataSize
)
2180 int nSize
= MultiByteToWideChar(
2182 a_pInputData
, (int) a_uInputDataLen
,
2183 (wchar_t *) a_pOutputData
, (int) a_uOutputDataSize
);
2187 /** Calculate the number of char required by the storage format of this
2188 * data. The storage format is always UTF-8.
2190 * @param a_pInputData NULL terminated string to calculate the number of
2191 * bytes required to be converted to storage format.
2192 * @return Number of bytes required by the string when
2193 * converted to storage format. This size always
2194 * includes space for the terminating NULL character.
2195 * @return -1 cast to size_t on a conversion error.
2198 const SI_CHAR
* a_pInputData
)
2200 int retval
= WideCharToMultiByte(
2202 (const wchar_t *) a_pInputData
, -1,
2204 return (size_t) (retval
> 0 ? retval
: -1);
2207 /** Convert the input string to the storage format of this data.
2208 * The storage format is always UTF-8 or MBCS.
2210 * @param a_pInputData NULL terminated source string to convert. All of
2211 * the data will be converted including the
2212 * terminating NULL character.
2213 * @param a_pOutputData Pointer to the buffer to receive the converted
2215 * @param a_pOutputDataSize Size of the output buffer in char.
2216 * @return true if all of the input data, including the
2217 * terminating NULL character was successfully
2220 bool ConvertToStore(
2221 const SI_CHAR
* a_pInputData
,
2222 char * a_pOutputData
,
2223 size_t a_uOutputDataSize
)
2225 int retval
= WideCharToMultiByte(
2227 (const wchar_t *) a_pInputData
, -1,
2228 a_pOutputData
, (int) a_uOutputDataSize
, 0, 0);
2233 #endif // SI_CONVERT_WIN32
2236 // ---------------------------------------------------------------------------
2238 // ---------------------------------------------------------------------------
2240 typedef CSimpleIniTempl
<char,
2241 SI_NoCase
<char>,SI_ConvertA
<char> > CSimpleIniA
;
2242 typedef CSimpleIniTempl
<char,
2243 SI_Case
<char>,SI_ConvertA
<char> > CSimpleIniCaseA
;
2245 #if defined(SI_CONVERT_ICU)
2246 typedef CSimpleIniTempl
<UChar
,
2247 SI_NoCase
<UChar
>,SI_ConvertW
<UChar
> > CSimpleIniW
;
2248 typedef CSimpleIniTempl
<UChar
,
2249 SI_Case
<UChar
>,SI_ConvertW
<UChar
> > CSimpleIniCaseW
;
2251 typedef CSimpleIniTempl
<wchar_t,
2252 SI_NoCase
<wchar_t>,SI_ConvertW
<wchar_t> > CSimpleIniW
;
2253 typedef CSimpleIniTempl
<wchar_t,
2254 SI_Case
<wchar_t>,SI_ConvertW
<wchar_t> > CSimpleIniCaseW
;
2258 # define CSimpleIni CSimpleIniW
2259 # define CSimpleIniCase CSimpleIniCaseW
2260 # define SI_NEWLINE SI_NEWLINE_W
2262 # define CSimpleIni CSimpleIniA
2263 # define CSimpleIniCase CSimpleIniCaseA
2264 # define SI_NEWLINE SI_NEWLINE_A
2267 #endif // INCLUDED_SimpleIni_h