Handle versioncheck.tortoisegit.org as official source
[TortoiseGit.git] / src / Utils / SimpleIni.h
blobc0833891572a3df77914873ce02a0b1f5bef7cbf
1 // Library: SimpleIni
2 // File: SimpleIni.h
3 // Author: Brodie Thiesfield <code@jellycan.com>
4 // Source: http://code.jellycan.com/simpleini/
5 // Version: 2.8
6 //
7 // INTRODUCTION
8 // ============
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.
13 // FEATURES
14 // ========
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)
35 // USAGE SUMMARY
36 // =============
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
42 // platforms.
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 // --------- -------------- ---------- --------- ---------------
50 // SI_CONVERT_GENERIC
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
55 // SI_CONVERT_WIN32
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
60 // SI_CONVERT_ICU
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)
86 // MULTI-LINE VALUES
87 // =================
88 // Values that span multiple lines are created using the following format.
90 // key = <<<ENDTAG
91 // .... multiline value ....
92 // ENDTAG
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
100 // or after it.
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
103 // file uses.
105 // NOTES
106 // =====
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.
124 // MIT LICENCE
125 // ===========
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.
157 #ifdef _MSC_VER
158 # pragma warning (disable: 4127 4702)
159 #endif
161 #include <string>
162 #include <map>
163 #include <list>
164 #include <assert.h>
166 enum SI_Error {
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"
179 #ifdef _WIN32
180 # define SI_NEWLINE_A "\r\n"
181 # define SI_NEWLINE_W L"\r\n"
182 #else // !_WIN32
183 # define SI_NEWLINE_A "\n"
184 # define SI_NEWLINE_W L"\n"
185 #endif // _WIN32
187 #if defined(_WIN32) || defined(SI_CONVERT_ICU)
188 # define SI_HAS_WIDE_LOADFILE
189 #endif
191 #if defined(SI_CONVERT_ICU)
192 # include <unicode/ustring.h>
193 #endif
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
220 public:
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. */
233 class OutputWriter {
234 public:
235 OutputWriter() { }
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 {
242 FILE * m_file;
243 public:
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;
253 public:
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 {
263 public:
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;
270 return *this;
272 bool ConvertToStore(const SI_CHAR * a_pszString) {
273 size_t uLen = SizeToStore(a_pszString);
274 if (uLen == (size_t)(-1)) {
275 return false;
277 while (uLen > m_scratch.size()) {
278 m_scratch.resize(m_scratch.size() * 2);
280 return SI_CONVERTER::ConvertToStore(
281 a_pszString,
282 const_cast<char*>(m_scratch.data()),
283 m_scratch.size());
285 const char * Data() { return m_scratch.data(); }
286 private:
287 std::string m_scratch;
290 public:
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);
301 * Destructor
303 ~CSimpleIniTempl();
306 * Deallocate all memory stored by this object
308 void Reset();
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:
338 * [section]
339 * test=value1
340 * test=value2
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
345 * at any time.
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
360 * at any time.
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
376 * current platform.
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);
394 #endif // _WIN32
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
404 SI_Error LoadFile(
405 const char * a_pData,
406 size_t a_uDataLen);
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:
412 * SI_CHAR FORMAT
413 * ------- ------
414 * char same format as when loaded (MBCS or UTF-8)
415 * wchar_t UTF-8
416 * other 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
435 * binary output.
436 * @param a_bUseBOM Prepend the UTF-8 BOM if the output stream is
437 * in UTF-8 format.
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);
444 return Save(writer);
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);
454 return Save(writer);
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
498 bool GetAllValues(
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
511 * empty section.
513 * @return SI_Error See error definitions
514 * @return SI_UPDATED Value was updated
515 * @return SI_INSERTED Value was inserted
517 SI_Error SetValue(
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
531 * in this situation.
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
539 * removed?
541 * @return true Key or section was deleted.
542 * @return false Key or section was not found.
544 bool Delete(
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
559 int GetSectionSize(
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
571 * are in use!
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
576 * name found.
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
589 * are in use!
591 * @param a_names Vector that will receive all of the section
592 * names. See note above!
594 void GetAllSections(
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
604 * are in use!
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!
610 void GetAllKeys(
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);
624 private:
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. */
631 bool FindEntry(
632 SI_CHAR *& a_pData,
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
639 * used as is. */
640 SI_Error AddEntry(
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;
673 private:
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.
679 SI_CHAR * m_pData;
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
683 * buffer.
685 size_t m_uDataLen;
687 /** Parsed INI data. Section -> (Key -> Value). */
688 TSection m_data;
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()
692 * has been called.
694 TNamesDepend m_strings;
696 /** Is the format of our datafile UTF-8 or MBCS? */
697 bool m_bStoreIsUtf8;
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 // ---------------------------------------------------------------------------
707 // IMPLEMENTATION
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)
713 : m_pData(0),
714 m_uDataLen(0),
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()
723 Reset();
726 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
727 void
728 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Reset()
730 // remove all data
731 delete[] m_pData;
732 m_pData = 0;
733 m_uDataLen = 0;
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>
749 SI_Error
750 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
751 const char * a_pszFile)
753 FILE * fp = fopen(a_pszFile, "rb");
754 if (!fp) {
755 return SI_FILE;
757 SI_Error rc = LoadFile(fp);
758 fclose(fp);
759 return rc;
762 #ifdef SI_HAS_WIDE_LOADFILE
763 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
764 SI_Error
765 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
766 const wchar_t * a_pwszFile)
768 #ifdef _WIN32
769 FILE * fp;
770 errno_t err = _wfopen_s(&fp, a_pwszFile, L"rb");
771 if ((err!=0)||(!fp)) {
772 return SI_FILE;
774 SI_Error rc = LoadFile(fp);
775 fclose(fp);
776 return rc;
777 #else // SI_CONVERT_ICU
778 char szFile[256];
779 u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
780 return LoadFile(szFile);
781 #endif
783 #endif // _WIN32
785 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
786 SI_Error
787 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
788 FILE * a_fpFile)
790 // load the raw file data
791 int retval = fseek(a_fpFile, 0, SEEK_END);
792 if (retval != 0) {
793 return SI_FILE;
795 long lSize = ftell(a_fpFile);
796 if (lSize < 0) {
797 return SI_FILE;
799 char * pData = new char[lSize];
800 if (!pData) {
801 return SI_NOMEM;
803 fseek(a_fpFile, 0, SEEK_SET);
804 size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
805 if (uRead != (size_t) lSize) {
806 delete[] pData;
807 return SI_FILE;
810 // convert the raw data to unicode
811 SI_Error rc = LoadFile(pData, uRead);
812 delete[] pData;
813 return rc;
816 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
817 SI_Error
818 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
819 const char * a_pData,
820 size_t a_uDataLen)
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) {
827 a_pData += 3;
828 a_uDataLen -= 3;
832 // determine the length of the converted data
833 size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
834 if (uLen == (size_t)(-1)) {
835 return SI_FAIL;
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];
841 if (!pData) {
842 return SI_NOMEM;
844 memset(pData, 0, sizeof(SI_CHAR)*(uLen+1));
846 // convert the data
847 if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
848 delete[] pData;
849 return SI_FAIL;
852 // parse it
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.
862 SI_Error rc;
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
870 if (bCopyStrings) {
871 delete[] pData;
873 else {
874 m_pData = pData;
875 m_uDataLen = uLen+1;
878 return SI_OK;
881 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
882 bool
883 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
884 SI_CHAR *& a_pData,
885 const SI_CHAR *& a_pSection,
886 const SI_CHAR *& a_pKey,
887 const SI_CHAR *& a_pVal ) const
889 SI_CHAR * pTrail;
890 while (*a_pData) {
891 // skip spaces and empty lines
892 while (*a_pData && IsSpace(*a_pData)) {
893 ++a_pData;
895 if (!*a_pData) {
896 break;
899 // skip comment lines
900 if (IsComment(*a_pData)) {
901 while (*a_pData && !IsNewLineChar(*a_pData)) {
902 ++a_pData;
904 continue;
907 // process section names
908 if (*a_pData == '[') {
909 // skip leading spaces
910 ++a_pData;
911 while (*a_pData && IsSpace(*a_pData)) {
912 ++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)) {
919 ++a_pData;
922 // if it's an invalid line, just skip it
923 if (*a_pData != ']') {
924 continue;
927 // remove trailing spaces from the section
928 pTrail = a_pData - 1;
929 while (pTrail >= a_pSection && IsSpace(*pTrail)) {
930 --pTrail;
932 ++pTrail;
933 *pTrail = 0;
935 // skip to the end of the line
936 ++a_pData; // safe as checked that it == ']' above
937 while (*a_pData && !IsNewLineChar(*a_pData)) {
938 ++a_pData;
941 a_pKey = 0;
942 a_pVal = 0;
943 return true;
946 // find the end of the key name (it may contain spaces)
947 // and convert it to lowercase as necessary
948 a_pKey = a_pData;
949 while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
950 ++a_pData;
953 // if it's an invalid line, just skip it
954 if (*a_pData != '=') {
955 continue;
958 // empty keys are invalid
959 if (a_pKey == a_pData) {
960 while (*a_pData && !IsNewLineChar(*a_pData)) {
961 ++a_pData;
963 continue;
966 // remove trailing spaces from the key
967 pTrail = a_pData - 1;
968 while (pTrail >= a_pKey && IsSpace(*pTrail)) {
969 --pTrail;
971 ++pTrail;
972 *pTrail = 0;
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)) {
977 ++a_pData;
980 // find the end of the value which is the end of this line
981 a_pVal = a_pData;
982 while (*a_pData && !IsNewLineChar(*a_pData)) {
983 ++a_pData;
986 // remove trailing spaces from the value
987 pTrail = a_pData - 1;
988 if (*a_pData) { // prepare for the next round
989 ++a_pData;
991 while (pTrail >= a_pVal && IsSpace(*pTrail)) {
992 --pTrail;
994 ++pTrail;
995 *pTrail = 0;
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
1003 return true;
1006 return false;
1009 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1010 bool
1011 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineTag(
1012 const SI_CHAR * a_pVal
1013 ) const
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;
1019 return true;
1022 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1023 bool
1024 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData(
1025 const SI_CHAR * a_pData
1026 ) const
1028 // data is multi-line if it has any of the following features:
1029 // * whitespace prefix
1030 // * embedded newlines
1031 // * whitespace suffix
1033 // empty string
1034 if (!*a_pData) {
1035 return false;
1038 // check for prefix
1039 if (IsSpace(*a_pData)) {
1040 return true;
1043 // embedded newlines
1044 while (*a_pData) {
1045 if (IsNewLineChar(*++a_pData)) {
1046 return true;
1050 // check for suffix
1051 if (IsSpace(*--a_pData)) {
1052 return true;
1055 return false;
1058 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1059 bool
1060 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar(
1061 SI_CHAR a_c ) const
1063 return a_c == '\n' || a_c == '\r';
1066 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1067 bool
1068 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindMultiLine(
1069 SI_CHAR *& a_pData,
1070 const SI_CHAR *& a_pVal
1071 ) const
1073 // skip the "<<<" to get the tag that will end the multiline
1074 const SI_CHAR * pTagName = a_pVal + 3;
1075 #ifdef _WIN32
1076 a_pData += 1; // skip the \n of the \r\n
1077 #endif
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.
1083 SI_CHAR *pLine;
1084 SI_CHAR cRememberThis;
1085 for(;;) {
1086 // find the beginning and end of this line
1087 while (IsNewLineChar(*a_pData)) ++a_pData;
1088 pLine = a_pData;
1089 while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
1091 // end the line with a NULL
1092 cRememberThis = *a_pData;
1093 *a_pData = 0;
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.
1100 --pLine;
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
1104 --pLine;
1106 *pLine = 0;
1108 if (cRememberThis) {
1109 ++a_pData;
1111 return true;
1114 // otherwise put the char back and continue checking
1115 if (!cRememberThis) {
1116 return false;
1118 *a_pData++ = cRememberThis;
1122 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1123 SI_Error
1124 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString(
1125 const SI_CHAR *& a_pString)
1127 size_t uLen = 0;
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);
1134 else {
1135 for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
1137 ++uLen; // NULL character
1138 SI_CHAR * pCopy = new SI_CHAR[uLen];
1139 if (!pCopy) {
1140 return SI_NOMEM;
1142 memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
1143 m_strings.push_back(pCopy);
1144 a_pString = pCopy;
1145 return SI_OK;
1148 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1149 SI_Error
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)
1156 SI_Error rc;
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 =
1175 m_data.insert(
1176 typename TSection::value_type( a_pSection, TKeyVal() ) );
1177 iSection = i.first;
1178 bInserted = true;
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));
1207 bInserted = true;
1209 iKey->second = a_pValue;
1210 return bInserted ? SI_INSERTED : SI_UPDATED;
1213 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1214 const SI_CHAR *
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) {
1225 return a_pDefault;
1227 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1228 if (iSection == m_data.end()) {
1229 return a_pDefault;
1231 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
1232 if (iKeyVal == iSection->second.end()) {
1233 return a_pDefault;
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>
1250 bool
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) {
1257 return false;
1259 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1260 if (iSection == m_data.end()) {
1261 return false;
1263 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
1264 if (iKeyVal == iSection->second.end()) {
1265 return false;
1268 // insert all values for this key
1269 a_values.push_back(iKeyVal->second);
1270 if (m_bAllowMultiKey) {
1271 ++iKeyVal;
1272 while (iKeyVal != iSection->second.end() && IsEqual(a_pKey, iKeyVal->first)) {
1273 a_values.push_back(iKeyVal->second);
1274 ++iKeyVal;
1277 return true;
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
1285 if (!a_pSection) {
1286 return -1;
1289 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1290 if (iSection == m_data.end()) {
1291 return -1;
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
1302 int nCount = 0;
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)) {
1307 ++nCount;
1308 pLastKey = iKeyVal->first;
1311 return nCount;
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
1319 if (a_pSection) {
1320 typename TSection::const_iterator i = m_data.find(a_pSection);
1321 if (i != m_data.end()) {
1322 return &(i->second);
1325 return 0;
1328 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1329 void
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>
1340 void
1341 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllKeys(
1342 const SI_CHAR * a_pSection,
1343 TNamesDepend & a_names ) const
1345 if (!a_pSection) {
1346 return;
1349 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1350 if (iSection == m_data.end()) {
1351 return;
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>
1366 SI_Error
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)) {
1379 return SI_FAIL;
1381 if (!bFirstLine) {
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) {
1393 // write the key
1394 if (!convert.ConvertToStore(iKeyVal->first)) {
1395 return SI_FAIL;
1397 a_oOutput.Write(convert.Data());
1399 // write the value
1400 if (!convert.ConvertToStore(iKeyVal->second)) {
1401 return SI_FAIL;
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");
1409 else {
1410 a_oOutput.Write(convert.Data());
1412 a_oOutput.Write(SI_NEWLINE_A);
1415 bFirstLine = false;
1418 return SI_OK;
1421 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1422 bool
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)
1428 if (!a_pSection) {
1429 return false;
1432 typename TSection::iterator iSection = m_data.find(a_pSection);
1433 if (iSection == m_data.end()) {
1434 return false;
1437 // remove a single key if we have a keyname
1438 if (a_pKey) {
1439 typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
1440 if (iKeyVal == iSection->second.end()) {
1441 return false;
1444 // remove any copied strings and then the key
1445 typename TKeyVal::iterator iDelete;
1446 do {
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
1458 // deletion code
1459 if (!a_bRemoveEmpty || !iSection->second.empty()) {
1460 return true;
1463 else {
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);
1477 return true;
1480 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1481 void
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);
1493 m_strings.erase(i);
1494 break;
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)
1515 # ifdef _WIN32
1516 # define SI_CONVERT_WIN32
1517 # else
1518 # define SI_CONVERT_GENERIC
1519 # endif
1520 #endif
1523 * Generic case-sensitive <less> comparison. This class returns numerically
1524 * ordered ASCII case-sensitive text for all possible sizes and types of
1525 * SI_CHAR.
1527 template<class SI_CHAR>
1528 struct SI_GenericCase {
1529 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
1530 long cmp;
1531 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
1532 cmp = (long) *pLeft - (long) *pRight;
1533 if (cmp != 0) {
1534 return cmp < 0;
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 {
1553 long cmp;
1554 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
1555 cmp = (long) locase(*pLeft) - (long) locase(*pRight);
1556 if (cmp != 0) {
1557 return cmp < 0;
1560 return *pRight != 0;
1565 * Null conversion class for MBCS/UTF-8 to char (or equivalent).
1567 template<class SI_CHAR>
1568 class SI_ConvertA {
1569 bool m_bStoreIsUtf8;
1570 protected:
1571 SI_ConvertA() { }
1572 public:
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;
1579 return *this;
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
1613 * converted data.
1614 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
1615 * @return true if all of the input data was successfully
1616 * converted.
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) {
1626 return false;
1628 memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
1629 return true;
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.
1642 size_t SizeToStore(
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
1656 * string.
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
1660 * converted.
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) {
1670 return false;
1673 // ascii/UTF-8 needs no conversion
1674 memcpy(a_pOutputData, a_pInputData, uInputLen);
1675 return true;
1680 // ---------------------------------------------------------------------------
1681 // SI_CONVERT_GENERIC
1682 // ---------------------------------------------------------------------------
1683 #ifdef SI_CONVERT_GENERIC
1685 #define SI_Case SI_GenericCase
1686 #define SI_NoCase SI_GenericNoCase
1688 #include <wchar.h>
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>
1696 class SI_ConvertW {
1697 bool m_bStoreIsUtf8;
1698 protected:
1699 SI_ConvertW() { }
1700 public:
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;
1707 return *this;
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
1732 // the source text.
1733 return a_uInputDataLen;
1735 else {
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
1748 * converted data.
1749 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
1750 * @return true if all of the input data was successfully
1751 * converted.
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,
1772 lenientConversion);
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,
1779 lenientConversion);
1781 return retval == conversionOK;
1783 else {
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.
1800 size_t SizeToStore(
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
1805 size_t uLen = 0;
1806 while (a_pInputData[uLen]) {
1807 ++uLen;
1809 return (6 * uLen) + 1;
1811 else {
1812 size_t uLen = wcstombs(NULL, a_pInputData, 0);
1813 if (uLen == (size_t)(-1)) {
1814 return uLen;
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
1827 * string.
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
1831 * converted.
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]) {
1842 ++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,
1858 lenientConversion);
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,
1865 lenientConversion);
1867 return retval == conversionOK;
1869 else {
1870 size_t retval = wcstombs(a_pOutputData,
1871 a_pInputData, a_uOutputDataSize);
1872 return retval != (size_t) -1;
1877 #endif // SI_CONVERT_GENERIC
1880 // ---------------------------------------------------------------------------
1881 // SI_CONVERT_ICU
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>
1894 class SI_ConvertW {
1895 const char * m_pEncoding;
1896 UConverter * m_pConverter;
1897 protected:
1898 SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
1899 public:
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;
1909 return *this;
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);
1932 UErrorCode nError;
1934 if (!m_pConverter) {
1935 nError = U_ZERO_ERROR;
1936 m_pConverter = ucnv_open(m_pEncoding, &nError);
1937 if (U_FAILURE(nError)) {
1938 return (size_t) -1;
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) {
1947 return (size_t) -1;
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
1961 * converted data.
1962 * @param a_uOutputDataSize Size of the output buffer in UChar.
1963 * @return true if all of the input data was successfully
1964 * converted.
1966 bool ConvertFromStore(
1967 const char * a_pInputData,
1968 size_t a_uInputDataLen,
1969 UChar * a_pOutputData,
1970 size_t a_uOutputDataSize)
1972 UErrorCode nError;
1974 if (!m_pConverter) {
1975 nError = U_ZERO_ERROR;
1976 m_pConverter = ucnv_open(m_pEncoding, &nError);
1977 if (U_FAILURE(nError)) {
1978 return false;
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)) {
1988 return false;
1991 return true;
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.
2004 size_t SizeToStore(
2005 const UChar * a_pInputData)
2007 UErrorCode nError;
2009 if (!m_pConverter) {
2010 nError = U_ZERO_ERROR;
2011 m_pConverter = ucnv_open(m_pEncoding, &nError);
2012 if (U_FAILURE(nError)) {
2013 return (size_t) -1;
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) {
2022 return (size_t) -1;
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
2035 * string.
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
2039 * converted.
2041 bool ConvertToStore(
2042 const UChar * a_pInputData,
2043 char * a_pOutputData,
2044 size_t a_uOutputDataSize)
2046 UErrorCode nError;
2048 if (!m_pConverter) {
2049 nError = U_ZERO_ERROR;
2050 m_pConverter = ucnv_open(m_pEncoding, &nError);
2051 if (U_FAILURE(nError)) {
2052 return false;
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)) {
2062 return false;
2065 return true;
2069 #endif // SI_CONVERT_ICU
2072 // ---------------------------------------------------------------------------
2073 // SI_CONVERT_WIN32
2074 // ---------------------------------------------------------------------------
2075 #ifdef SI_CONVERT_WIN32
2077 #define SI_Case SI_GenericCase
2079 // Windows CE doesn't have errno or MBCS libraries
2080 #ifdef _WIN32_WCE
2081 # ifndef SI_NO_MBCS
2082 # define SI_NO_MBCS
2083 # endif
2084 #endif
2086 #include <windows.h>
2087 #ifdef SI_NO_MBCS
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>
2100 struct SI_NoCase {
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>
2122 class SI_ConvertW {
2123 UINT m_uCodePage;
2124 protected:
2125 SI_ConvertW() { }
2126 public:
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;
2135 return *this;
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(
2158 m_uCodePage, 0,
2159 a_pInputData, (int) a_uInputDataLen,
2160 0, 0);
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
2172 * converted data.
2173 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2174 * @return true if all of the input data was successfully
2175 * converted.
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(
2184 m_uCodePage, 0,
2185 a_pInputData, (int) a_uInputDataLen,
2186 (wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
2187 return (nSize > 0);
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.
2200 size_t SizeToStore(
2201 const SI_CHAR * a_pInputData)
2203 int retval = WideCharToMultiByte(
2204 m_uCodePage, 0,
2205 (const wchar_t *) a_pInputData, -1,
2206 0, 0, 0, 0);
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
2217 * string.
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
2221 * converted.
2223 bool ConvertToStore(
2224 const SI_CHAR * a_pInputData,
2225 char * a_pOutputData,
2226 size_t a_uOutputDataSize)
2228 int retval = WideCharToMultiByte(
2229 m_uCodePage, 0,
2230 (const wchar_t *) a_pInputData, -1,
2231 a_pOutputData, (int) a_uOutputDataSize, 0, 0);
2232 return retval > 0;
2236 #endif // SI_CONVERT_WIN32
2239 // ---------------------------------------------------------------------------
2240 // TYPE DEFINITIONS
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;
2253 #else
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;
2258 #endif
2260 #ifdef _UNICODE
2261 # define CSimpleIni CSimpleIniW
2262 # define CSimpleIniCase CSimpleIniCaseW
2263 # define SI_NEWLINE SI_NEWLINE_W
2264 #else // !_UNICODE
2265 # define CSimpleIni CSimpleIniA
2266 # define CSimpleIniCase CSimpleIniCaseA
2267 # define SI_NEWLINE SI_NEWLINE_A
2268 #endif // _UNICODE
2270 #endif // INCLUDED_SimpleIni_h