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;
1076 a_pData
+= 1; // skip the \n of the \r\n
1078 a_pVal
= a_pData
; // real value starts on next line
1080 // find the end tag. This tag must start in column 1 and be
1081 // followed by a newline. No whitespace removal is done while
1082 // searching for this tag.
1084 SI_CHAR cRememberThis
;
1086 // find the beginning and end of this line
1087 while (IsNewLineChar(*a_pData
)) ++a_pData
;
1089 while (*a_pData
&& !IsNewLineChar(*a_pData
)) ++a_pData
;
1091 // end the line with a NULL
1092 cRememberThis
= *a_pData
;
1095 // see if we have found the tag
1096 if (IsEqual(pLine
, pTagName
)) {
1097 // null terminate the data before the newline of the previous line.
1098 // If you want a new line at the end of the line then add an empty
1099 // line before the tag.
1101 if (*(pLine
-1) == '\r') {
1102 // handle Windows style newlines. This handles Unix newline files
1103 // on Windows and Windows style newlines on Unix. \n\r
1108 if (cRememberThis
) {
1114 // otherwise put the char back and continue checking
1115 if (!cRememberThis
) {
1118 *a_pData
++ = cRememberThis
;
1122 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1124 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::CopyString(
1125 const SI_CHAR
*& a_pString
)
1128 if (sizeof(SI_CHAR
) == sizeof(char)) {
1129 uLen
= strlen((const char *)a_pString
);
1131 else if (sizeof(SI_CHAR
) == sizeof(wchar_t)) {
1132 uLen
= wcslen((const wchar_t *)a_pString
);
1135 for ( ; a_pString
[uLen
]; ++uLen
) /*loop*/ ;
1137 ++uLen
; // NULL character
1138 SI_CHAR
* pCopy
= new SI_CHAR
[uLen
];
1142 memcpy(pCopy
, a_pString
, sizeof(SI_CHAR
)*uLen
);
1143 m_strings
.push_back(pCopy
);
1148 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1150 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::AddEntry(
1151 const SI_CHAR
* a_pSection
,
1152 const SI_CHAR
* a_pKey
,
1153 const SI_CHAR
* a_pValue
,
1154 bool a_bCopyStrings
)
1157 bool bInserted
= false;
1159 // check for existence of the section first if we need string copies
1160 typename
TSection::iterator iSection
= m_data
.end();
1161 if (a_bCopyStrings
) {
1162 iSection
= m_data
.find(a_pSection
);
1163 if (iSection
== m_data
.end()) {
1164 // if the section doesn't exist then we need a copy as the
1165 // string needs to last beyond the end of this function
1166 // because we will be inserting the section next
1167 rc
= CopyString(a_pSection
);
1168 if (rc
< 0) return rc
;
1172 // create the section entry
1173 if (iSection
== m_data
.end()) {
1174 std::pair
<typename
TSection::iterator
,bool> i
=
1176 typename
TSection::value_type( a_pSection
, TKeyVal() ) );
1180 if (!a_pKey
|| !a_pValue
) {
1181 // section only entries are specified with pKey and pVal as NULL
1182 return bInserted
? SI_INSERTED
: SI_UPDATED
;
1185 // check for existence of the key
1186 TKeyVal
& keyval
= iSection
->second
;
1187 typename
TKeyVal::iterator iKey
= keyval
.find(a_pKey
);
1189 // make string copies if necessary
1190 if (a_bCopyStrings
) {
1191 if (m_bAllowMultiKey
|| iKey
== keyval
.end()) {
1192 // if the key doesn't exist then we need a copy as the
1193 // string needs to last beyond the end of this function
1194 // because we will be inserting the key next
1195 rc
= CopyString(a_pKey
);
1196 if (rc
< 0) return rc
;
1199 // we always need a copy of the value
1200 rc
= CopyString(a_pValue
);
1201 if (rc
< 0) return rc
;
1204 // create the key entry
1205 if (iKey
== keyval
.end() || m_bAllowMultiKey
) {
1206 iKey
= keyval
.insert(typename
TKeyVal::value_type(a_pKey
, (const SI_CHAR
*)0));
1209 iKey
->second
= a_pValue
;
1210 return bInserted
? SI_INSERTED
: SI_UPDATED
;
1213 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1215 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetValue(
1216 const SI_CHAR
* a_pSection
,
1217 const SI_CHAR
* a_pKey
,
1218 const SI_CHAR
* a_pDefault
,
1219 bool * a_pHasMultiple
) const
1221 if (a_pHasMultiple
) {
1222 *a_pHasMultiple
= false;
1224 if (!a_pSection
|| !a_pKey
) {
1227 typename
TSection::const_iterator iSection
= m_data
.find(a_pSection
);
1228 if (iSection
== m_data
.end()) {
1231 typename
TKeyVal::const_iterator iKeyVal
= iSection
->second
.find(a_pKey
);
1232 if (iKeyVal
== iSection
->second
.end()) {
1236 // check for multiple entries with the same key
1237 if (m_bAllowMultiKey
&& a_pHasMultiple
) {
1238 typename
TKeyVal::const_iterator iTemp
= iKeyVal
;
1239 if (++iTemp
!= iSection
->second
.end()) {
1240 if (IsEqual(a_pKey
, iTemp
->first
)) {
1241 *a_pHasMultiple
= true;
1246 return iKeyVal
->second
;
1249 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1251 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetAllValues(
1252 const SI_CHAR
* a_pSection
,
1253 const SI_CHAR
* a_pKey
,
1254 TNamesDepend
& a_values
) const
1256 if (!a_pSection
|| !a_pKey
) {
1259 typename
TSection::const_iterator iSection
= m_data
.find(a_pSection
);
1260 if (iSection
== m_data
.end()) {
1263 typename
TKeyVal::const_iterator iKeyVal
= iSection
->second
.find(a_pKey
);
1264 if (iKeyVal
== iSection
->second
.end()) {
1268 // insert all values for this key
1269 a_values
.push_back(iKeyVal
->second
);
1270 if (m_bAllowMultiKey
) {
1272 while (iKeyVal
!= iSection
->second
.end() && IsEqual(a_pKey
, iKeyVal
->first
)) {
1273 a_values
.push_back(iKeyVal
->second
);
1280 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1282 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetSectionSize(
1283 const SI_CHAR
* a_pSection
) const
1289 typename
TSection::const_iterator iSection
= m_data
.find(a_pSection
);
1290 if (iSection
== m_data
.end()) {
1293 const TKeyVal
& section
= iSection
->second
;
1295 // if multi-key isn't permitted then the section size is
1296 // the number of keys that we have.
1297 if (!m_bAllowMultiKey
|| section
.empty()) {
1298 return (int) section
.size();
1301 // otherwise we need to count them
1303 const SI_CHAR
* pLastKey
= 0;
1304 typename
TKeyVal::const_iterator iKeyVal
= section
.begin();
1305 for (int n
= 0; iKeyVal
!= section
.end(); ++iKeyVal
, ++n
) {
1306 if (!pLastKey
|| !IsEqual(pLastKey
, iKeyVal
->first
)) {
1308 pLastKey
= iKeyVal
->first
;
1314 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1315 const typename CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::TKeyVal
*
1316 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetSection(
1317 const SI_CHAR
* a_pSection
) const
1320 typename
TSection::const_iterator i
= m_data
.find(a_pSection
);
1321 if (i
!= m_data
.end()) {
1322 return &(i
->second
);
1328 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1330 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetAllSections(
1331 TNamesDepend
& a_names
) const
1333 typename
TSection::const_iterator i
= m_data
.begin();
1334 for (int n
= 0; i
!= m_data
.end(); ++i
, ++n
) {
1335 a_names
.push_back(i
->first
);
1339 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1341 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::GetAllKeys(
1342 const SI_CHAR
* a_pSection
,
1343 TNamesDepend
& a_names
) const
1349 typename
TSection::const_iterator iSection
= m_data
.find(a_pSection
);
1350 if (iSection
== m_data
.end()) {
1354 const TKeyVal
& section
= iSection
->second
;
1355 const SI_CHAR
* pLastKey
= 0;
1356 typename
TKeyVal::const_iterator iKeyVal
= section
.begin();
1357 for (int n
= 0; iKeyVal
!= section
.end(); ++iKeyVal
, ++n
) {
1358 if (!pLastKey
|| !IsEqual(pLastKey
, iKeyVal
->first
)) {
1359 a_names
.push_back(iKeyVal
->first
);
1360 pLastKey
= iKeyVal
->first
;
1365 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1367 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::Save(
1368 OutputWriter
& a_oOutput
) const
1370 Converter
convert(m_bStoreIsUtf8
);
1372 // iterate through our sections and output the data
1373 bool bFirstLine
= true;
1374 typename
TSection::const_iterator iSection
= m_data
.begin();
1375 for ( ; iSection
!= m_data
.end(); ++iSection
) {
1376 // write the section (unless there is no section name)
1377 if (iSection
->first
[0]) {
1378 if (!convert
.ConvertToStore(iSection
->first
)) {
1382 a_oOutput
.Write(SI_NEWLINE_A
);
1384 a_oOutput
.Write("[");
1385 a_oOutput
.Write(convert
.Data());
1386 a_oOutput
.Write("]");
1387 a_oOutput
.Write(SI_NEWLINE_A
);
1390 // write all keys and values
1391 typename
TKeyVal::const_iterator iKeyVal
= iSection
->second
.begin();
1392 for ( ; iKeyVal
!= iSection
->second
.end(); ++iKeyVal
) {
1394 if (!convert
.ConvertToStore(iKeyVal
->first
)) {
1397 a_oOutput
.Write(convert
.Data());
1400 if (!convert
.ConvertToStore(iKeyVal
->second
)) {
1403 a_oOutput
.Write("=");
1404 if (m_bAllowMultiLine
&& IsMultiLineData(iKeyVal
->second
)) {
1405 a_oOutput
.Write("<<<SI-END-OF-MULTILINE-TEXT" SI_NEWLINE_A
);
1406 a_oOutput
.Write(convert
.Data());
1407 a_oOutput
.Write(SI_NEWLINE_A
"SI-END-OF-MULTILINE-TEXT");
1410 a_oOutput
.Write(convert
.Data());
1412 a_oOutput
.Write(SI_NEWLINE_A
);
1421 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1423 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::Delete(
1424 const SI_CHAR
* a_pSection
,
1425 const SI_CHAR
* a_pKey
,
1426 bool a_bRemoveEmpty
)
1432 typename
TSection::iterator iSection
= m_data
.find(a_pSection
);
1433 if (iSection
== m_data
.end()) {
1437 // remove a single key if we have a keyname
1439 typename
TKeyVal::iterator iKeyVal
= iSection
->second
.find(a_pKey
);
1440 if (iKeyVal
== iSection
->second
.end()) {
1444 // remove any copied strings and then the key
1445 typename
TKeyVal::iterator iDelete
;
1447 iDelete
= iKeyVal
++;
1449 DeleteString(iDelete
->first
);
1450 DeleteString(iDelete
->second
);
1451 iSection
->second
.erase(iDelete
);
1453 while (iKeyVal
!= iSection
->second
.end()
1454 && IsEqual(a_pKey
, iKeyVal
->first
));
1456 // done now if the section is not empty or we are not pruning away
1457 // the empty sections. Otherwise let it fall through into the section
1459 if (!a_bRemoveEmpty
|| !iSection
->second
.empty()) {
1464 // delete all copied strings from this section. The actual
1465 // entries will be removed when the section is removed.
1466 typename
TKeyVal::iterator iKeyVal
= iSection
->second
.begin();
1467 for ( ; iKeyVal
!= iSection
->second
.end(); ++iKeyVal
) {
1468 DeleteString(iKeyVal
->first
);
1469 DeleteString(iKeyVal
->second
);
1473 // delete the section itself
1474 DeleteString(iSection
->first
);
1475 m_data
.erase(iSection
);
1480 template<class SI_CHAR
, class SI_STRLESS
, class SI_CONVERTER
>
1482 CSimpleIniTempl
<SI_CHAR
,SI_STRLESS
,SI_CONVERTER
>::DeleteString(
1483 const SI_CHAR
* a_pString
)
1485 // strings may exist either inside the data block, or they will be
1486 // individually allocated and stored in m_strings. We only physically
1487 // delete those stored in m_strings.
1488 if (a_pString
< m_pData
|| a_pString
>= m_pData
+ m_uDataLen
) {
1489 typename
TNamesDepend::iterator i
= m_strings
.begin();
1490 for (;i
!= m_strings
.end(); ++i
) {
1491 if (a_pString
== *i
) {
1492 delete[] const_cast<SI_CHAR
*>(*i
);
1500 // ---------------------------------------------------------------------------
1501 // CONVERSION FUNCTIONS
1502 // ---------------------------------------------------------------------------
1504 // Defines the conversion classes for different libraries. Before including
1505 // SimpleIni.h, set the converter that you wish you use by defining one of the
1506 // following symbols.
1508 // SI_CONVERT_GENERIC Use the Unicode reference conversion library in
1509 // the accompanying files ConvertUTF.h/c
1510 // SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
1511 // ICU headers on include path and icuuc.lib
1512 // SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
1514 #if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
1516 # define SI_CONVERT_WIN32
1518 # define SI_CONVERT_GENERIC
1523 * Generic case-sensitive <less> comparison. This class returns numerically
1524 * ordered ASCII case-sensitive text for all possible sizes and types of
1527 template<class SI_CHAR
>
1528 struct SI_GenericCase
{
1529 bool operator()(const SI_CHAR
* pLeft
, const SI_CHAR
* pRight
) const {
1531 for ( ;*pLeft
&& *pRight
; ++pLeft
, ++pRight
) {
1532 cmp
= (long) *pLeft
- (long) *pRight
;
1537 return *pRight
!= 0;
1542 * Generic ASCII case-insensitive <less> comparison. This class returns
1543 * numerically ordered ASCII case-insensitive text for all possible sizes
1544 * and types of SI_CHAR. It is not safe for MBCS text comparison where
1545 * ASCII A-Z characters are used in the encoding of multi-byte characters.
1547 template<class SI_CHAR
>
1548 struct SI_GenericNoCase
{
1549 inline SI_CHAR
locase(SI_CHAR ch
) const {
1550 return (ch
< 'A' || ch
> 'Z') ? ch
: (ch
- 'A' + 'a');
1552 bool operator()(const SI_CHAR
* pLeft
, const SI_CHAR
* pRight
) const {
1554 for ( ;*pLeft
&& *pRight
; ++pLeft
, ++pRight
) {
1555 cmp
= (long) locase(*pLeft
) - (long) locase(*pRight
);
1560 return *pRight
!= 0;
1565 * Null conversion class for MBCS/UTF-8 to char (or equivalent).
1567 template<class SI_CHAR
>
1569 bool m_bStoreIsUtf8
;
1573 SI_ConvertA(bool a_bStoreIsUtf8
) : m_bStoreIsUtf8(a_bStoreIsUtf8
) { }
1575 /* copy and assignment */
1576 SI_ConvertA(const SI_ConvertA
& rhs
) { operator=(rhs
); }
1577 SI_ConvertA
& operator=(const SI_ConvertA
& rhs
) {
1578 m_bStoreIsUtf8
= rhs
.m_bStoreIsUtf8
;
1582 /** Calculate the number of SI_CHAR required for converting the input
1583 * from the storage format. The storage format is always UTF-8 or MBCS.
1585 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1586 * @param a_uInputDataLen Length of storage format data in bytes. This
1587 * must be the actual length of the data, including
1588 * NULL byte if NULL terminated string is required.
1589 * @return Number of SI_CHAR required by the string when
1590 * converted. If there are embedded NULL bytes in the
1591 * input data, only the string up and not including
1592 * the NULL byte will be converted.
1593 * @return -1 cast to size_t on a conversion error.
1595 size_t SizeFromStore(
1596 const char * /*a_pInputData*/,
1597 size_t a_uInputDataLen
)
1599 assert(a_uInputDataLen
!= (size_t) -1);
1601 // ASCII/MBCS/UTF-8 needs no conversion
1602 return a_uInputDataLen
;
1605 /** Convert the input string from the storage format to SI_CHAR.
1606 * The storage format is always UTF-8 or MBCS.
1608 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1609 * @param a_uInputDataLen Length of storage format data in bytes. This
1610 * must be the actual length of the data, including
1611 * NULL byte if NULL terminated string is required.
1612 * @param a_pOutputData Pointer to the output buffer to received the
1614 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
1615 * @return true if all of the input data was successfully
1618 bool ConvertFromStore(
1619 const char * a_pInputData
,
1620 size_t a_uInputDataLen
,
1621 SI_CHAR
* a_pOutputData
,
1622 size_t a_uOutputDataSize
)
1624 // ASCII/MBCS/UTF-8 needs no conversion
1625 if (a_uInputDataLen
> a_uOutputDataSize
) {
1628 memcpy(a_pOutputData
, a_pInputData
, a_uInputDataLen
);
1632 /** Calculate the number of char required by the storage format of this
1633 * data. The storage format is always UTF-8 or MBCS.
1635 * @param a_pInputData NULL terminated string to calculate the number of
1636 * bytes required to be converted to storage format.
1637 * @return Number of bytes required by the string when
1638 * converted to storage format. This size always
1639 * includes space for the terminating NULL character.
1640 * @return -1 cast to size_t on a conversion error.
1643 const SI_CHAR
* a_pInputData
)
1645 // ASCII/MBCS/UTF-8 needs no conversion
1646 return strlen((const char *)a_pInputData
) + 1;
1649 /** Convert the input string to the storage format of this data.
1650 * The storage format is always UTF-8 or MBCS.
1652 * @param a_pInputData NULL terminated source string to convert. All of
1653 * the data will be converted including the
1654 * terminating NULL character.
1655 * @param a_pOutputData Pointer to the buffer to receive the converted
1657 * @param a_pOutputDataSize Size of the output buffer in char.
1658 * @return true if all of the input data, including the
1659 * terminating NULL character was successfully
1662 bool ConvertToStore(
1663 const SI_CHAR
* a_pInputData
,
1664 char * a_pOutputData
,
1665 size_t a_uOutputDataSize
)
1667 // calc input string length (SI_CHAR type and size independent)
1668 size_t uInputLen
= strlen((const char *)a_pInputData
) + 1;
1669 if (uInputLen
> a_uOutputDataSize
) {
1673 // ascii/UTF-8 needs no conversion
1674 memcpy(a_pOutputData
, a_pInputData
, uInputLen
);
1680 // ---------------------------------------------------------------------------
1681 // SI_CONVERT_GENERIC
1682 // ---------------------------------------------------------------------------
1683 #ifdef SI_CONVERT_GENERIC
1685 #define SI_Case SI_GenericCase
1686 #define SI_NoCase SI_GenericNoCase
1689 #include "ConvertUTF.h"
1692 * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference
1693 * library functions. This can be used on all platforms.
1695 template<class SI_CHAR
>
1697 bool m_bStoreIsUtf8
;
1701 SI_ConvertW(bool a_bStoreIsUtf8
) : m_bStoreIsUtf8(a_bStoreIsUtf8
) { }
1703 /* copy and assignment */
1704 SI_ConvertW(const SI_ConvertW
& rhs
) { operator=(rhs
); }
1705 SI_ConvertW
& operator=(const SI_ConvertW
& rhs
) {
1706 m_bStoreIsUtf8
= rhs
.m_bStoreIsUtf8
;
1710 /** Calculate the number of SI_CHAR required for converting the input
1711 * from the storage format. The storage format is always UTF-8 or MBCS.
1713 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1714 * @param a_uInputDataLen Length of storage format data in bytes. This
1715 * must be the actual length of the data, including
1716 * NULL byte if NULL terminated string is required.
1717 * @return Number of SI_CHAR required by the string when
1718 * converted. If there are embedded NULL bytes in the
1719 * input data, only the string up and not including
1720 * the NULL byte will be converted.
1721 * @return -1 cast to size_t on a conversion error.
1723 size_t SizeFromStore(
1724 const char * a_pInputData
,
1725 size_t a_uInputDataLen
)
1727 assert(a_uInputDataLen
!= (size_t) -1);
1729 if (m_bStoreIsUtf8
) {
1730 // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
1731 // so we just return the same number of characters required as for
1733 return a_uInputDataLen
;
1736 return mbstowcs(NULL
, a_pInputData
, a_uInputDataLen
);
1740 /** Convert the input string from the storage format to SI_CHAR.
1741 * The storage format is always UTF-8 or MBCS.
1743 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1744 * @param a_uInputDataLen Length of storage format data in bytes. This
1745 * must be the actual length of the data, including
1746 * NULL byte if NULL terminated string is required.
1747 * @param a_pOutputData Pointer to the output buffer to received the
1749 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
1750 * @return true if all of the input data was successfully
1753 bool ConvertFromStore(
1754 const char * a_pInputData
,
1755 size_t a_uInputDataLen
,
1756 SI_CHAR
* a_pOutputData
,
1757 size_t a_uOutputDataSize
)
1759 if (m_bStoreIsUtf8
) {
1760 // This uses the Unicode reference implementation to do the
1761 // conversion from UTF-8 to wchar_t. The required files are
1762 // ConvertUTF.h and ConvertUTF.c which should be included in
1763 // the distribution but are publically available from unicode.org
1764 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
1765 ConversionResult retval
;
1766 const UTF8
* pUtf8
= (const UTF8
*) a_pInputData
;
1767 if (sizeof(wchar_t) == sizeof(UTF32
)) {
1768 UTF32
* pUtf32
= (UTF32
*) a_pOutputData
;
1769 retval
= ConvertUTF8toUTF32(
1770 &pUtf8
, pUtf8
+ a_uInputDataLen
,
1771 &pUtf32
, pUtf32
+ a_uOutputDataSize
,
1774 else if (sizeof(wchar_t) == sizeof(UTF16
)) {
1775 UTF16
* pUtf16
= (UTF16
*) a_pOutputData
;
1776 retval
= ConvertUTF8toUTF16(
1777 &pUtf8
, pUtf8
+ a_uInputDataLen
,
1778 &pUtf16
, pUtf16
+ a_uOutputDataSize
,
1781 return retval
== conversionOK
;
1784 size_t retval
= mbstowcs(a_pOutputData
,
1785 a_pInputData
, a_uOutputDataSize
);
1786 return retval
!= (size_t)(-1);
1790 /** Calculate the number of char required by the storage format of this
1791 * data. The storage format is always UTF-8 or MBCS.
1793 * @param a_pInputData NULL terminated string to calculate the number of
1794 * bytes required to be converted to storage format.
1795 * @return Number of bytes required by the string when
1796 * converted to storage format. This size always
1797 * includes space for the terminating NULL character.
1798 * @return -1 cast to size_t on a conversion error.
1801 const SI_CHAR
* a_pInputData
)
1803 if (m_bStoreIsUtf8
) {
1804 // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
1806 while (a_pInputData
[uLen
]) {
1809 return (6 * uLen
) + 1;
1812 size_t uLen
= wcstombs(NULL
, a_pInputData
, 0);
1813 if (uLen
== (size_t)(-1)) {
1816 return uLen
+ 1; // include NULL terminator
1820 /** Convert the input string to the storage format of this data.
1821 * The storage format is always UTF-8 or MBCS.
1823 * @param a_pInputData NULL terminated source string to convert. All of
1824 * the data will be converted including the
1825 * terminating NULL character.
1826 * @param a_pOutputData Pointer to the buffer to receive the converted
1828 * @param a_pOutputDataSize Size of the output buffer in char.
1829 * @return true if all of the input data, including the
1830 * terminating NULL character was successfully
1833 bool ConvertToStore(
1834 const SI_CHAR
* a_pInputData
,
1835 char * a_pOutputData
,
1836 size_t a_uOutputDataSize
)
1838 if (m_bStoreIsUtf8
) {
1839 // calc input string length (SI_CHAR type and size independent)
1840 size_t uInputLen
= 0;
1841 while (a_pInputData
[uInputLen
]) {
1844 ++uInputLen
; // include the NULL char
1846 // This uses the Unicode reference implementation to do the
1847 // conversion from wchar_t to UTF-8. The required files are
1848 // ConvertUTF.h and ConvertUTF.c which should be included in
1849 // the distribution but are publically available from unicode.org
1850 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
1851 ConversionResult retval
;
1852 UTF8
* pUtf8
= (UTF8
*) a_pOutputData
;
1853 if (sizeof(wchar_t) == sizeof(UTF32
)) {
1854 const UTF32
* pUtf32
= (const UTF32
*) a_pInputData
;
1855 retval
= ConvertUTF32toUTF8(
1856 &pUtf32
, pUtf32
+ uInputLen
+ 1,
1857 &pUtf8
, pUtf8
+ a_uOutputDataSize
,
1860 else if (sizeof(wchar_t) == sizeof(UTF16
)) {
1861 const UTF16
* pUtf16
= (const UTF16
*) a_pInputData
;
1862 retval
= ConvertUTF16toUTF8(
1863 &pUtf16
, pUtf16
+ uInputLen
+ 1,
1864 &pUtf8
, pUtf8
+ a_uOutputDataSize
,
1867 return retval
== conversionOK
;
1870 size_t retval
= wcstombs(a_pOutputData
,
1871 a_pInputData
, a_uOutputDataSize
);
1872 return retval
!= (size_t) -1;
1877 #endif // SI_CONVERT_GENERIC
1880 // ---------------------------------------------------------------------------
1882 // ---------------------------------------------------------------------------
1883 #ifdef SI_CONVERT_ICU
1885 #define SI_Case SI_GenericCase
1886 #define SI_NoCase SI_GenericNoCase
1888 #include <unicode/ucnv.h>
1891 * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms.
1893 template<class SI_CHAR
>
1895 const char * m_pEncoding
;
1896 UConverter
* m_pConverter
;
1898 SI_ConvertW() : m_pEncoding(NULL
), m_pConverter(NULL
) { }
1900 SI_ConvertW(bool a_bStoreIsUtf8
) : m_pConverter(NULL
) {
1901 m_pEncoding
= a_bStoreIsUtf8
? "UTF-8" : NULL
;
1904 /* copy and assignment */
1905 SI_ConvertW(const SI_ConvertW
& rhs
) { operator=(rhs
); }
1906 SI_ConvertW
& operator=(const SI_ConvertW
& rhs
) {
1907 m_pEncoding
= rhs
.m_pEncoding
;
1908 m_pConverter
= NULL
;
1911 ~SI_ConvertW() { if (m_pConverter
) ucnv_close(m_pConverter
); }
1913 /** Calculate the number of UChar required for converting the input
1914 * from the storage format. The storage format is always UTF-8 or MBCS.
1916 * @param a_pInputData Data in storage format to be converted to UChar.
1917 * @param a_uInputDataLen Length of storage format data in bytes. This
1918 * must be the actual length of the data, including
1919 * NULL byte if NULL terminated string is required.
1920 * @return Number of UChar required by the string when
1921 * converted. If there are embedded NULL bytes in the
1922 * input data, only the string up and not including
1923 * the NULL byte will be converted.
1924 * @return -1 cast to size_t on a conversion error.
1926 size_t SizeFromStore(
1927 const char * a_pInputData
,
1928 size_t a_uInputDataLen
)
1930 assert(a_uInputDataLen
!= (size_t) -1);
1934 if (!m_pConverter
) {
1935 nError
= U_ZERO_ERROR
;
1936 m_pConverter
= ucnv_open(m_pEncoding
, &nError
);
1937 if (U_FAILURE(nError
)) {
1942 nError
= U_ZERO_ERROR
;
1943 ucnv_resetToUnicode(m_pConverter
);
1944 int32_t nLen
= ucnv_toUChars(m_pConverter
, NULL
, 0,
1945 a_pInputData
, (int32_t) a_uInputDataLen
, &nError
);
1946 if (nError
!= U_BUFFER_OVERFLOW_ERROR
) {
1950 return (size_t) nLen
;
1953 /** Convert the input string from the storage format to UChar.
1954 * The storage format is always UTF-8 or MBCS.
1956 * @param a_pInputData Data in storage format to be converted to UChar.
1957 * @param a_uInputDataLen Length of storage format data in bytes. This
1958 * must be the actual length of the data, including
1959 * NULL byte if NULL terminated string is required.
1960 * @param a_pOutputData Pointer to the output buffer to received the
1962 * @param a_uOutputDataSize Size of the output buffer in UChar.
1963 * @return true if all of the input data was successfully
1966 bool ConvertFromStore(
1967 const char * a_pInputData
,
1968 size_t a_uInputDataLen
,
1969 UChar
* a_pOutputData
,
1970 size_t a_uOutputDataSize
)
1974 if (!m_pConverter
) {
1975 nError
= U_ZERO_ERROR
;
1976 m_pConverter
= ucnv_open(m_pEncoding
, &nError
);
1977 if (U_FAILURE(nError
)) {
1982 nError
= U_ZERO_ERROR
;
1983 ucnv_resetToUnicode(m_pConverter
);
1984 ucnv_toUChars(m_pConverter
,
1985 a_pOutputData
, (int32_t) a_uOutputDataSize
,
1986 a_pInputData
, (int32_t) a_uInputDataLen
, &nError
);
1987 if (U_FAILURE(nError
)) {
1994 /** Calculate the number of char required by the storage format of this
1995 * data. The storage format is always UTF-8 or MBCS.
1997 * @param a_pInputData NULL terminated string to calculate the number of
1998 * bytes required to be converted to storage format.
1999 * @return Number of bytes required by the string when
2000 * converted to storage format. This size always
2001 * includes space for the terminating NULL character.
2002 * @return -1 cast to size_t on a conversion error.
2005 const UChar
* a_pInputData
)
2009 if (!m_pConverter
) {
2010 nError
= U_ZERO_ERROR
;
2011 m_pConverter
= ucnv_open(m_pEncoding
, &nError
);
2012 if (U_FAILURE(nError
)) {
2017 nError
= U_ZERO_ERROR
;
2018 ucnv_resetFromUnicode(m_pConverter
);
2019 int32_t nLen
= ucnv_fromUChars(m_pConverter
, NULL
, 0,
2020 a_pInputData
, -1, &nError
);
2021 if (nError
!= U_BUFFER_OVERFLOW_ERROR
) {
2025 return (size_t) nLen
+ 1;
2028 /** Convert the input string to the storage format of this data.
2029 * The storage format is always UTF-8 or MBCS.
2031 * @param a_pInputData NULL terminated source string to convert. All of
2032 * the data will be converted including the
2033 * terminating NULL character.
2034 * @param a_pOutputData Pointer to the buffer to receive the converted
2036 * @param a_pOutputDataSize Size of the output buffer in char.
2037 * @return true if all of the input data, including the
2038 * terminating NULL character was successfully
2041 bool ConvertToStore(
2042 const UChar
* a_pInputData
,
2043 char * a_pOutputData
,
2044 size_t a_uOutputDataSize
)
2048 if (!m_pConverter
) {
2049 nError
= U_ZERO_ERROR
;
2050 m_pConverter
= ucnv_open(m_pEncoding
, &nError
);
2051 if (U_FAILURE(nError
)) {
2056 nError
= U_ZERO_ERROR
;
2057 ucnv_resetFromUnicode(m_pConverter
);
2058 ucnv_fromUChars(m_pConverter
,
2059 a_pOutputData
, (int32_t) a_uOutputDataSize
,
2060 a_pInputData
, -1, &nError
);
2061 if (U_FAILURE(nError
)) {
2069 #endif // SI_CONVERT_ICU
2072 // ---------------------------------------------------------------------------
2074 // ---------------------------------------------------------------------------
2075 #ifdef SI_CONVERT_WIN32
2077 #define SI_Case SI_GenericCase
2079 // Windows CE doesn't have errno or MBCS libraries
2086 #include <windows.h>
2088 # define SI_NoCase SI_GenericNoCase
2089 #else // !SI_NO_MBCS
2091 * Case-insensitive comparison class using Win32 MBCS functions. This class
2092 * returns a case-insensitive semi-collation order for MBCS text. It may not
2093 * be safe for UTF-8 text returned in char format as we don't know what
2094 * characters will be folded by the function! Therefore, if you are using
2095 * SI_CHAR == char and SetUnicode(true), then you need to use the generic
2096 * SI_NoCase class instead.
2098 #include <mbstring.h>
2099 template<class SI_CHAR
>
2101 bool operator()(const SI_CHAR
* pLeft
, const SI_CHAR
* pRight
) const {
2102 if (sizeof(SI_CHAR
) == sizeof(char)) {
2103 return _mbsicmp((const unsigned char *)pLeft
,
2104 (const unsigned char *)pRight
) < 0;
2106 if (sizeof(SI_CHAR
) == sizeof(wchar_t)) {
2107 return _wcsicmp((const wchar_t *)pLeft
,
2108 (const wchar_t *)pRight
) < 0;
2110 return SI_GenericNoCase
<SI_CHAR
>()(pLeft
, pRight
);
2113 #endif // SI_NO_MBCS
2116 * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses
2117 * only the Win32 functions and doesn't require the external Unicode UTF-8
2118 * conversion library. It will not work on Windows 95 without using Microsoft
2119 * Layer for Unicode in your application.
2121 template<class SI_CHAR
>
2127 SI_ConvertW(bool a_bStoreIsUtf8
) {
2128 m_uCodePage
= a_bStoreIsUtf8
? CP_UTF8
: CP_ACP
;
2131 /* copy and assignment */
2132 SI_ConvertW(const SI_ConvertW
& rhs
) { operator=(rhs
); }
2133 SI_ConvertW
& operator=(const SI_ConvertW
& rhs
) {
2134 m_uCodePage
= rhs
.m_uCodePage
;
2138 /** Calculate the number of SI_CHAR required for converting the input
2139 * from the storage format. The storage format is always UTF-8 or MBCS.
2141 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2142 * @param a_uInputDataLen Length of storage format data in bytes. This
2143 * must be the actual length of the data, including
2144 * NULL byte if NULL terminated string is required.
2145 * @return Number of SI_CHAR required by the string when
2146 * converted. If there are embedded NULL bytes in the
2147 * input data, only the string up and not including
2148 * the NULL byte will be converted.
2149 * @return -1 cast to size_t on a conversion error.
2151 size_t SizeFromStore(
2152 const char * a_pInputData
,
2153 size_t a_uInputDataLen
)
2155 assert(a_uInputDataLen
!= (size_t) -1);
2157 int retval
= MultiByteToWideChar(
2159 a_pInputData
, (int) a_uInputDataLen
,
2161 return (size_t)(retval
> 0 ? retval
: -1);
2164 /** Convert the input string from the storage format to SI_CHAR.
2165 * The storage format is always UTF-8 or MBCS.
2167 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2168 * @param a_uInputDataLen Length of storage format data in bytes. This
2169 * must be the actual length of the data, including
2170 * NULL byte if NULL terminated string is required.
2171 * @param a_pOutputData Pointer to the output buffer to received the
2173 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2174 * @return true if all of the input data was successfully
2177 bool ConvertFromStore(
2178 const char * a_pInputData
,
2179 size_t a_uInputDataLen
,
2180 SI_CHAR
* a_pOutputData
,
2181 size_t a_uOutputDataSize
)
2183 int nSize
= MultiByteToWideChar(
2185 a_pInputData
, (int) a_uInputDataLen
,
2186 (wchar_t *) a_pOutputData
, (int) a_uOutputDataSize
);
2190 /** Calculate the number of char required by the storage format of this
2191 * data. The storage format is always UTF-8.
2193 * @param a_pInputData NULL terminated string to calculate the number of
2194 * bytes required to be converted to storage format.
2195 * @return Number of bytes required by the string when
2196 * converted to storage format. This size always
2197 * includes space for the terminating NULL character.
2198 * @return -1 cast to size_t on a conversion error.
2201 const SI_CHAR
* a_pInputData
)
2203 int retval
= WideCharToMultiByte(
2205 (const wchar_t *) a_pInputData
, -1,
2207 return (size_t) (retval
> 0 ? retval
: -1);
2210 /** Convert the input string to the storage format of this data.
2211 * The storage format is always UTF-8 or MBCS.
2213 * @param a_pInputData NULL terminated source string to convert. All of
2214 * the data will be converted including the
2215 * terminating NULL character.
2216 * @param a_pOutputData Pointer to the buffer to receive the converted
2218 * @param a_pOutputDataSize Size of the output buffer in char.
2219 * @return true if all of the input data, including the
2220 * terminating NULL character was successfully
2223 bool ConvertToStore(
2224 const SI_CHAR
* a_pInputData
,
2225 char * a_pOutputData
,
2226 size_t a_uOutputDataSize
)
2228 int retval
= WideCharToMultiByte(
2230 (const wchar_t *) a_pInputData
, -1,
2231 a_pOutputData
, (int) a_uOutputDataSize
, 0, 0);
2236 #endif // SI_CONVERT_WIN32
2239 // ---------------------------------------------------------------------------
2241 // ---------------------------------------------------------------------------
2243 typedef CSimpleIniTempl
<char,
2244 SI_NoCase
<char>,SI_ConvertA
<char> > CSimpleIniA
;
2245 typedef CSimpleIniTempl
<char,
2246 SI_Case
<char>,SI_ConvertA
<char> > CSimpleIniCaseA
;
2248 #if defined(SI_CONVERT_ICU)
2249 typedef CSimpleIniTempl
<UChar
,
2250 SI_NoCase
<UChar
>,SI_ConvertW
<UChar
> > CSimpleIniW
;
2251 typedef CSimpleIniTempl
<UChar
,
2252 SI_Case
<UChar
>,SI_ConvertW
<UChar
> > CSimpleIniCaseW
;
2254 typedef CSimpleIniTempl
<wchar_t,
2255 SI_NoCase
<wchar_t>,SI_ConvertW
<wchar_t> > CSimpleIniW
;
2256 typedef CSimpleIniTempl
<wchar_t,
2257 SI_Case
<wchar_t>,SI_ConvertW
<wchar_t> > CSimpleIniCaseW
;
2261 # define CSimpleIni CSimpleIniW
2262 # define CSimpleIniCase CSimpleIniCaseW
2263 # define SI_NEWLINE SI_NEWLINE_W
2265 # define CSimpleIni CSimpleIniA
2266 # define CSimpleIniCase CSimpleIniCaseA
2267 # define SI_NEWLINE SI_NEWLINE_A
2270 #endif // INCLUDED_SimpleIni_h