Search ssh executable on the PATH configured in TortoiseGit
[TortoiseGit.git] / src / Utils / SimpleIni.h
blobbd181caeaa9f747aa0613810bdb572289b22ca20
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 a_pVal = a_pData; // real value starts on next line
1077 // find the end tag. This tag must start in column 1 and be
1078 // followed by a newline. No whitespace removal is done while
1079 // searching for this tag.
1080 SI_CHAR *pLine;
1081 SI_CHAR cRememberThis;
1082 for(;;) {
1083 // find the beginning and end of this line
1084 while (IsNewLineChar(*a_pData)) ++a_pData;
1085 pLine = a_pData;
1086 while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
1088 // end the line with a NULL
1089 cRememberThis = *a_pData;
1090 *a_pData = 0;
1092 // see if we have found the tag
1093 if (IsEqual(pLine, pTagName)) {
1094 // null terminate the data before the newline of the previous line.
1095 // If you want a new line at the end of the line then add an empty
1096 // line before the tag.
1097 --pLine;
1098 if (*(pLine-1) == '\r') {
1099 // handle Windows style newlines. This handles Unix newline files
1100 // on Windows and Windows style newlines on Unix. \n\r
1101 --pLine;
1103 *pLine = 0;
1105 if (cRememberThis) {
1106 ++a_pData;
1108 return true;
1111 // otherwise put the char back and continue checking
1112 if (!cRememberThis) {
1113 return false;
1115 *a_pData++ = cRememberThis;
1119 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1120 SI_Error
1121 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString(
1122 const SI_CHAR *& a_pString)
1124 size_t uLen = 0;
1125 if (sizeof(SI_CHAR) == sizeof(char)) {
1126 uLen = strlen((const char *)a_pString);
1128 else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
1129 uLen = wcslen((const wchar_t *)a_pString);
1131 else {
1132 for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
1134 ++uLen; // NULL character
1135 SI_CHAR * pCopy = new SI_CHAR[uLen];
1136 if (!pCopy) {
1137 return SI_NOMEM;
1139 memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
1140 m_strings.push_back(pCopy);
1141 a_pString = pCopy;
1142 return SI_OK;
1145 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1146 SI_Error
1147 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
1148 const SI_CHAR * a_pSection,
1149 const SI_CHAR * a_pKey,
1150 const SI_CHAR * a_pValue,
1151 bool a_bCopyStrings)
1153 SI_Error rc;
1154 bool bInserted = false;
1156 // check for existence of the section first if we need string copies
1157 typename TSection::iterator iSection = m_data.end();
1158 if (a_bCopyStrings) {
1159 iSection = m_data.find(a_pSection);
1160 if (iSection == m_data.end()) {
1161 // if the section doesn't exist then we need a copy as the
1162 // string needs to last beyond the end of this function
1163 // because we will be inserting the section next
1164 rc = CopyString(a_pSection);
1165 if (rc < 0) return rc;
1169 // create the section entry
1170 if (iSection == m_data.end()) {
1171 std::pair<typename TSection::iterator,bool> i =
1172 m_data.insert(
1173 typename TSection::value_type( a_pSection, TKeyVal() ) );
1174 iSection = i.first;
1175 bInserted = true;
1177 if (!a_pKey || !a_pValue) {
1178 // section only entries are specified with pKey and pVal as NULL
1179 return bInserted ? SI_INSERTED : SI_UPDATED;
1182 // check for existence of the key
1183 TKeyVal & keyval = iSection->second;
1184 typename TKeyVal::iterator iKey = keyval.find(a_pKey);
1186 // make string copies if necessary
1187 if (a_bCopyStrings) {
1188 if (m_bAllowMultiKey || iKey == keyval.end()) {
1189 // if the key doesn't exist then we need a copy as the
1190 // string needs to last beyond the end of this function
1191 // because we will be inserting the key next
1192 rc = CopyString(a_pKey);
1193 if (rc < 0) return rc;
1196 // we always need a copy of the value
1197 rc = CopyString(a_pValue);
1198 if (rc < 0) return rc;
1201 // create the key entry
1202 if (iKey == keyval.end() || m_bAllowMultiKey) {
1203 iKey = keyval.insert(typename TKeyVal::value_type(a_pKey, (const SI_CHAR *)0));
1204 bInserted = true;
1206 iKey->second = a_pValue;
1207 return bInserted ? SI_INSERTED : SI_UPDATED;
1210 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1211 const SI_CHAR *
1212 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetValue(
1213 const SI_CHAR * a_pSection,
1214 const SI_CHAR * a_pKey,
1215 const SI_CHAR * a_pDefault,
1216 bool * a_pHasMultiple) const
1218 if (a_pHasMultiple) {
1219 *a_pHasMultiple = false;
1221 if (!a_pSection || !a_pKey) {
1222 return a_pDefault;
1224 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1225 if (iSection == m_data.end()) {
1226 return a_pDefault;
1228 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
1229 if (iKeyVal == iSection->second.end()) {
1230 return a_pDefault;
1233 // check for multiple entries with the same key
1234 if (m_bAllowMultiKey && a_pHasMultiple) {
1235 typename TKeyVal::const_iterator iTemp = iKeyVal;
1236 if (++iTemp != iSection->second.end()) {
1237 if (IsEqual(a_pKey, iTemp->first)) {
1238 *a_pHasMultiple = true;
1243 return iKeyVal->second;
1246 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1247 bool
1248 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues(
1249 const SI_CHAR * a_pSection,
1250 const SI_CHAR * a_pKey,
1251 TNamesDepend & a_values) const
1253 if (!a_pSection || !a_pKey) {
1254 return false;
1256 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1257 if (iSection == m_data.end()) {
1258 return false;
1260 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
1261 if (iKeyVal == iSection->second.end()) {
1262 return false;
1265 // insert all values for this key
1266 a_values.push_back(iKeyVal->second);
1267 if (m_bAllowMultiKey) {
1268 ++iKeyVal;
1269 while (iKeyVal != iSection->second.end() && IsEqual(a_pKey, iKeyVal->first)) {
1270 a_values.push_back(iKeyVal->second);
1271 ++iKeyVal;
1274 return true;
1277 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1279 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSectionSize(
1280 const SI_CHAR * a_pSection ) const
1282 if (!a_pSection) {
1283 return -1;
1286 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1287 if (iSection == m_data.end()) {
1288 return -1;
1290 const TKeyVal & section = iSection->second;
1292 // if multi-key isn't permitted then the section size is
1293 // the number of keys that we have.
1294 if (!m_bAllowMultiKey || section.empty()) {
1295 return (int) section.size();
1298 // otherwise we need to count them
1299 int nCount = 0;
1300 const SI_CHAR * pLastKey = 0;
1301 typename TKeyVal::const_iterator iKeyVal = section.begin();
1302 for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
1303 if (!pLastKey || !IsEqual(pLastKey, iKeyVal->first)) {
1304 ++nCount;
1305 pLastKey = iKeyVal->first;
1308 return nCount;
1311 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1312 const typename CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::TKeyVal *
1313 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSection(
1314 const SI_CHAR * a_pSection) const
1316 if (a_pSection) {
1317 typename TSection::const_iterator i = m_data.find(a_pSection);
1318 if (i != m_data.end()) {
1319 return &(i->second);
1322 return 0;
1325 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1326 void
1327 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections(
1328 TNamesDepend & a_names ) const
1330 typename TSection::const_iterator i = m_data.begin();
1331 for (int n = 0; i != m_data.end(); ++i, ++n ) {
1332 a_names.push_back(i->first);
1336 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1337 void
1338 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllKeys(
1339 const SI_CHAR * a_pSection,
1340 TNamesDepend & a_names ) const
1342 if (!a_pSection) {
1343 return;
1346 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1347 if (iSection == m_data.end()) {
1348 return;
1351 const TKeyVal & section = iSection->second;
1352 const SI_CHAR * pLastKey = 0;
1353 typename TKeyVal::const_iterator iKeyVal = section.begin();
1354 for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) {
1355 if (!pLastKey || !IsEqual(pLastKey, iKeyVal->first)) {
1356 a_names.push_back(iKeyVal->first);
1357 pLastKey = iKeyVal->first;
1362 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1363 SI_Error
1364 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
1365 OutputWriter & a_oOutput) const
1367 Converter convert(m_bStoreIsUtf8);
1369 // iterate through our sections and output the data
1370 bool bFirstLine = true;
1371 typename TSection::const_iterator iSection = m_data.begin();
1372 for ( ; iSection != m_data.end(); ++iSection ) {
1373 // write the section (unless there is no section name)
1374 if (iSection->first[0]) {
1375 if (!convert.ConvertToStore(iSection->first)) {
1376 return SI_FAIL;
1378 if (!bFirstLine) {
1379 a_oOutput.Write(SI_NEWLINE_A);
1381 a_oOutput.Write("[");
1382 a_oOutput.Write(convert.Data());
1383 a_oOutput.Write("]");
1384 a_oOutput.Write(SI_NEWLINE_A);
1387 // write all keys and values
1388 typename TKeyVal::const_iterator iKeyVal = iSection->second.begin();
1389 for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
1390 // write the key
1391 if (!convert.ConvertToStore(iKeyVal->first)) {
1392 return SI_FAIL;
1394 a_oOutput.Write(convert.Data());
1396 // write the value
1397 if (!convert.ConvertToStore(iKeyVal->second)) {
1398 return SI_FAIL;
1400 a_oOutput.Write("=");
1401 if (m_bAllowMultiLine && IsMultiLineData(iKeyVal->second)) {
1402 a_oOutput.Write("<<<SI-END-OF-MULTILINE-TEXT" SI_NEWLINE_A);
1403 a_oOutput.Write(convert.Data());
1404 a_oOutput.Write(SI_NEWLINE_A "SI-END-OF-MULTILINE-TEXT");
1406 else {
1407 a_oOutput.Write(convert.Data());
1409 a_oOutput.Write(SI_NEWLINE_A);
1412 bFirstLine = false;
1415 return SI_OK;
1418 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1419 bool
1420 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(
1421 const SI_CHAR * a_pSection,
1422 const SI_CHAR * a_pKey,
1423 bool a_bRemoveEmpty)
1425 if (!a_pSection) {
1426 return false;
1429 typename TSection::iterator iSection = m_data.find(a_pSection);
1430 if (iSection == m_data.end()) {
1431 return false;
1434 // remove a single key if we have a keyname
1435 if (a_pKey) {
1436 typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
1437 if (iKeyVal == iSection->second.end()) {
1438 return false;
1441 // remove any copied strings and then the key
1442 typename TKeyVal::iterator iDelete;
1443 do {
1444 iDelete = iKeyVal++;
1446 DeleteString(iDelete->first);
1447 DeleteString(iDelete->second);
1448 iSection->second.erase(iDelete);
1450 while (iKeyVal != iSection->second.end()
1451 && IsEqual(a_pKey, iKeyVal->first));
1453 // done now if the section is not empty or we are not pruning away
1454 // the empty sections. Otherwise let it fall through into the section
1455 // deletion code
1456 if (!a_bRemoveEmpty || !iSection->second.empty()) {
1457 return true;
1460 else {
1461 // delete all copied strings from this section. The actual
1462 // entries will be removed when the section is removed.
1463 typename TKeyVal::iterator iKeyVal = iSection->second.begin();
1464 for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
1465 DeleteString(iKeyVal->first);
1466 DeleteString(iKeyVal->second);
1470 // delete the section itself
1471 DeleteString(iSection->first);
1472 m_data.erase(iSection);
1474 return true;
1477 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1478 void
1479 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString(
1480 const SI_CHAR * a_pString )
1482 // strings may exist either inside the data block, or they will be
1483 // individually allocated and stored in m_strings. We only physically
1484 // delete those stored in m_strings.
1485 if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
1486 typename TNamesDepend::iterator i = m_strings.begin();
1487 for (;i != m_strings.end(); ++i) {
1488 if (a_pString == *i) {
1489 delete[] const_cast<SI_CHAR*>(*i);
1490 m_strings.erase(i);
1491 break;
1497 // ---------------------------------------------------------------------------
1498 // CONVERSION FUNCTIONS
1499 // ---------------------------------------------------------------------------
1501 // Defines the conversion classes for different libraries. Before including
1502 // SimpleIni.h, set the converter that you wish you use by defining one of the
1503 // following symbols.
1505 // SI_CONVERT_GENERIC Use the Unicode reference conversion library in
1506 // the accompanying files ConvertUTF.h/c
1507 // SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
1508 // ICU headers on include path and icuuc.lib
1509 // SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
1511 #if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
1512 # ifdef _WIN32
1513 # define SI_CONVERT_WIN32
1514 # else
1515 # define SI_CONVERT_GENERIC
1516 # endif
1517 #endif
1520 * Generic case-sensitive <less> comparison. This class returns numerically
1521 * ordered ASCII case-sensitive text for all possible sizes and types of
1522 * SI_CHAR.
1524 template<class SI_CHAR>
1525 struct SI_GenericCase {
1526 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
1527 long cmp;
1528 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
1529 cmp = (long) *pLeft - (long) *pRight;
1530 if (cmp != 0) {
1531 return cmp < 0;
1534 return *pRight != 0;
1539 * Generic ASCII case-insensitive <less> comparison. This class returns
1540 * numerically ordered ASCII case-insensitive text for all possible sizes
1541 * and types of SI_CHAR. It is not safe for MBCS text comparison where
1542 * ASCII A-Z characters are used in the encoding of multi-byte characters.
1544 template<class SI_CHAR>
1545 struct SI_GenericNoCase {
1546 inline SI_CHAR locase(SI_CHAR ch) const {
1547 return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
1549 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
1550 long cmp;
1551 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
1552 cmp = (long) locase(*pLeft) - (long) locase(*pRight);
1553 if (cmp != 0) {
1554 return cmp < 0;
1557 return *pRight != 0;
1562 * Null conversion class for MBCS/UTF-8 to char (or equivalent).
1564 template<class SI_CHAR>
1565 class SI_ConvertA {
1566 bool m_bStoreIsUtf8;
1567 protected:
1568 SI_ConvertA() { }
1569 public:
1570 SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
1572 /* copy and assignment */
1573 SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }
1574 SI_ConvertA & operator=(const SI_ConvertA & rhs) {
1575 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
1576 return *this;
1579 /** Calculate the number of SI_CHAR required for converting the input
1580 * from the storage format. The storage format is always UTF-8 or MBCS.
1582 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1583 * @param a_uInputDataLen Length of storage format data in bytes. This
1584 * must be the actual length of the data, including
1585 * NULL byte if NULL terminated string is required.
1586 * @return Number of SI_CHAR required by the string when
1587 * converted. If there are embedded NULL bytes in the
1588 * input data, only the string up and not including
1589 * the NULL byte will be converted.
1590 * @return -1 cast to size_t on a conversion error.
1592 size_t SizeFromStore(
1593 const char * /*a_pInputData*/,
1594 size_t a_uInputDataLen)
1596 assert(a_uInputDataLen != (size_t) -1);
1598 // ASCII/MBCS/UTF-8 needs no conversion
1599 return a_uInputDataLen;
1602 /** Convert the input string from the storage format to SI_CHAR.
1603 * The storage format is always UTF-8 or MBCS.
1605 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1606 * @param a_uInputDataLen Length of storage format data in bytes. This
1607 * must be the actual length of the data, including
1608 * NULL byte if NULL terminated string is required.
1609 * @param a_pOutputData Pointer to the output buffer to received the
1610 * converted data.
1611 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
1612 * @return true if all of the input data was successfully
1613 * converted.
1615 bool ConvertFromStore(
1616 const char * a_pInputData,
1617 size_t a_uInputDataLen,
1618 SI_CHAR * a_pOutputData,
1619 size_t a_uOutputDataSize)
1621 // ASCII/MBCS/UTF-8 needs no conversion
1622 if (a_uInputDataLen > a_uOutputDataSize) {
1623 return false;
1625 memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
1626 return true;
1629 /** Calculate the number of char required by the storage format of this
1630 * data. The storage format is always UTF-8 or MBCS.
1632 * @param a_pInputData NULL terminated string to calculate the number of
1633 * bytes required to be converted to storage format.
1634 * @return Number of bytes required by the string when
1635 * converted to storage format. This size always
1636 * includes space for the terminating NULL character.
1637 * @return -1 cast to size_t on a conversion error.
1639 size_t SizeToStore(
1640 const SI_CHAR * a_pInputData)
1642 // ASCII/MBCS/UTF-8 needs no conversion
1643 return strlen((const char *)a_pInputData) + 1;
1646 /** Convert the input string to the storage format of this data.
1647 * The storage format is always UTF-8 or MBCS.
1649 * @param a_pInputData NULL terminated source string to convert. All of
1650 * the data will be converted including the
1651 * terminating NULL character.
1652 * @param a_pOutputData Pointer to the buffer to receive the converted
1653 * string.
1654 * @param a_pOutputDataSize Size of the output buffer in char.
1655 * @return true if all of the input data, including the
1656 * terminating NULL character was successfully
1657 * converted.
1659 bool ConvertToStore(
1660 const SI_CHAR * a_pInputData,
1661 char * a_pOutputData,
1662 size_t a_uOutputDataSize)
1664 // calc input string length (SI_CHAR type and size independent)
1665 size_t uInputLen = strlen((const char *)a_pInputData) + 1;
1666 if (uInputLen > a_uOutputDataSize) {
1667 return false;
1670 // ascii/UTF-8 needs no conversion
1671 memcpy(a_pOutputData, a_pInputData, uInputLen);
1672 return true;
1677 // ---------------------------------------------------------------------------
1678 // SI_CONVERT_GENERIC
1679 // ---------------------------------------------------------------------------
1680 #ifdef SI_CONVERT_GENERIC
1682 #define SI_Case SI_GenericCase
1683 #define SI_NoCase SI_GenericNoCase
1685 #include <wchar.h>
1686 #include "ConvertUTF.h"
1689 * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference
1690 * library functions. This can be used on all platforms.
1692 template<class SI_CHAR>
1693 class SI_ConvertW {
1694 bool m_bStoreIsUtf8;
1695 protected:
1696 SI_ConvertW() { }
1697 public:
1698 SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
1700 /* copy and assignment */
1701 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
1702 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
1703 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
1704 return *this;
1707 /** Calculate the number of SI_CHAR required for converting the input
1708 * from the storage format. The storage format is always UTF-8 or MBCS.
1710 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1711 * @param a_uInputDataLen Length of storage format data in bytes. This
1712 * must be the actual length of the data, including
1713 * NULL byte if NULL terminated string is required.
1714 * @return Number of SI_CHAR required by the string when
1715 * converted. If there are embedded NULL bytes in the
1716 * input data, only the string up and not including
1717 * the NULL byte will be converted.
1718 * @return -1 cast to size_t on a conversion error.
1720 size_t SizeFromStore(
1721 const char * a_pInputData,
1722 size_t a_uInputDataLen)
1724 assert(a_uInputDataLen != (size_t) -1);
1726 if (m_bStoreIsUtf8) {
1727 // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
1728 // so we just return the same number of characters required as for
1729 // the source text.
1730 return a_uInputDataLen;
1732 else {
1733 return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
1737 /** Convert the input string from the storage format to SI_CHAR.
1738 * The storage format is always UTF-8 or MBCS.
1740 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1741 * @param a_uInputDataLen Length of storage format data in bytes. This
1742 * must be the actual length of the data, including
1743 * NULL byte if NULL terminated string is required.
1744 * @param a_pOutputData Pointer to the output buffer to received the
1745 * converted data.
1746 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
1747 * @return true if all of the input data was successfully
1748 * converted.
1750 bool ConvertFromStore(
1751 const char * a_pInputData,
1752 size_t a_uInputDataLen,
1753 SI_CHAR * a_pOutputData,
1754 size_t a_uOutputDataSize)
1756 if (m_bStoreIsUtf8) {
1757 // This uses the Unicode reference implementation to do the
1758 // conversion from UTF-8 to wchar_t. The required files are
1759 // ConvertUTF.h and ConvertUTF.c which should be included in
1760 // the distribution but are publically available from unicode.org
1761 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
1762 ConversionResult retval;
1763 const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
1764 if (sizeof(wchar_t) == sizeof(UTF32)) {
1765 UTF32 * pUtf32 = (UTF32 *) a_pOutputData;
1766 retval = ConvertUTF8toUTF32(
1767 &pUtf8, pUtf8 + a_uInputDataLen,
1768 &pUtf32, pUtf32 + a_uOutputDataSize,
1769 lenientConversion);
1771 else if (sizeof(wchar_t) == sizeof(UTF16)) {
1772 UTF16 * pUtf16 = (UTF16 *) a_pOutputData;
1773 retval = ConvertUTF8toUTF16(
1774 &pUtf8, pUtf8 + a_uInputDataLen,
1775 &pUtf16, pUtf16 + a_uOutputDataSize,
1776 lenientConversion);
1778 return retval == conversionOK;
1780 else {
1781 size_t retval = mbstowcs(a_pOutputData,
1782 a_pInputData, a_uOutputDataSize);
1783 return retval != (size_t)(-1);
1787 /** Calculate the number of char required by the storage format of this
1788 * data. The storage format is always UTF-8 or MBCS.
1790 * @param a_pInputData NULL terminated string to calculate the number of
1791 * bytes required to be converted to storage format.
1792 * @return Number of bytes required by the string when
1793 * converted to storage format. This size always
1794 * includes space for the terminating NULL character.
1795 * @return -1 cast to size_t on a conversion error.
1797 size_t SizeToStore(
1798 const SI_CHAR * a_pInputData)
1800 if (m_bStoreIsUtf8) {
1801 // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
1802 size_t uLen = 0;
1803 while (a_pInputData[uLen]) {
1804 ++uLen;
1806 return (6 * uLen) + 1;
1808 else {
1809 size_t uLen = wcstombs(NULL, a_pInputData, 0);
1810 if (uLen == (size_t)(-1)) {
1811 return uLen;
1813 return uLen + 1; // include NULL terminator
1817 /** Convert the input string to the storage format of this data.
1818 * The storage format is always UTF-8 or MBCS.
1820 * @param a_pInputData NULL terminated source string to convert. All of
1821 * the data will be converted including the
1822 * terminating NULL character.
1823 * @param a_pOutputData Pointer to the buffer to receive the converted
1824 * string.
1825 * @param a_pOutputDataSize Size of the output buffer in char.
1826 * @return true if all of the input data, including the
1827 * terminating NULL character was successfully
1828 * converted.
1830 bool ConvertToStore(
1831 const SI_CHAR * a_pInputData,
1832 char * a_pOutputData,
1833 size_t a_uOutputDataSize)
1835 if (m_bStoreIsUtf8) {
1836 // calc input string length (SI_CHAR type and size independent)
1837 size_t uInputLen = 0;
1838 while (a_pInputData[uInputLen]) {
1839 ++uInputLen;
1841 ++uInputLen; // include the NULL char
1843 // This uses the Unicode reference implementation to do the
1844 // conversion from wchar_t to UTF-8. The required files are
1845 // ConvertUTF.h and ConvertUTF.c which should be included in
1846 // the distribution but are publically available from unicode.org
1847 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
1848 ConversionResult retval;
1849 UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
1850 if (sizeof(wchar_t) == sizeof(UTF32)) {
1851 const UTF32 * pUtf32 = (const UTF32 *) a_pInputData;
1852 retval = ConvertUTF32toUTF8(
1853 &pUtf32, pUtf32 + uInputLen + 1,
1854 &pUtf8, pUtf8 + a_uOutputDataSize,
1855 lenientConversion);
1857 else if (sizeof(wchar_t) == sizeof(UTF16)) {
1858 const UTF16 * pUtf16 = (const UTF16 *) a_pInputData;
1859 retval = ConvertUTF16toUTF8(
1860 &pUtf16, pUtf16 + uInputLen + 1,
1861 &pUtf8, pUtf8 + a_uOutputDataSize,
1862 lenientConversion);
1864 return retval == conversionOK;
1866 else {
1867 size_t retval = wcstombs(a_pOutputData,
1868 a_pInputData, a_uOutputDataSize);
1869 return retval != (size_t) -1;
1874 #endif // SI_CONVERT_GENERIC
1877 // ---------------------------------------------------------------------------
1878 // SI_CONVERT_ICU
1879 // ---------------------------------------------------------------------------
1880 #ifdef SI_CONVERT_ICU
1882 #define SI_Case SI_GenericCase
1883 #define SI_NoCase SI_GenericNoCase
1885 #include <unicode/ucnv.h>
1888 * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms.
1890 template<class SI_CHAR>
1891 class SI_ConvertW {
1892 const char * m_pEncoding;
1893 UConverter * m_pConverter;
1894 protected:
1895 SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
1896 public:
1897 SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
1898 m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
1901 /* copy and assignment */
1902 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
1903 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
1904 m_pEncoding = rhs.m_pEncoding;
1905 m_pConverter = NULL;
1906 return *this;
1908 ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); }
1910 /** Calculate the number of UChar required for converting the input
1911 * from the storage format. The storage format is always UTF-8 or MBCS.
1913 * @param a_pInputData Data in storage format to be converted to UChar.
1914 * @param a_uInputDataLen Length of storage format data in bytes. This
1915 * must be the actual length of the data, including
1916 * NULL byte if NULL terminated string is required.
1917 * @return Number of UChar required by the string when
1918 * converted. If there are embedded NULL bytes in the
1919 * input data, only the string up and not including
1920 * the NULL byte will be converted.
1921 * @return -1 cast to size_t on a conversion error.
1923 size_t SizeFromStore(
1924 const char * a_pInputData,
1925 size_t a_uInputDataLen)
1927 assert(a_uInputDataLen != (size_t) -1);
1929 UErrorCode nError;
1931 if (!m_pConverter) {
1932 nError = U_ZERO_ERROR;
1933 m_pConverter = ucnv_open(m_pEncoding, &nError);
1934 if (U_FAILURE(nError)) {
1935 return (size_t) -1;
1939 nError = U_ZERO_ERROR;
1940 ucnv_resetToUnicode(m_pConverter);
1941 int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,
1942 a_pInputData, (int32_t) a_uInputDataLen, &nError);
1943 if (nError != U_BUFFER_OVERFLOW_ERROR) {
1944 return (size_t) -1;
1947 return (size_t) nLen;
1950 /** Convert the input string from the storage format to UChar.
1951 * The storage format is always UTF-8 or MBCS.
1953 * @param a_pInputData Data in storage format to be converted to UChar.
1954 * @param a_uInputDataLen Length of storage format data in bytes. This
1955 * must be the actual length of the data, including
1956 * NULL byte if NULL terminated string is required.
1957 * @param a_pOutputData Pointer to the output buffer to received the
1958 * converted data.
1959 * @param a_uOutputDataSize Size of the output buffer in UChar.
1960 * @return true if all of the input data was successfully
1961 * converted.
1963 bool ConvertFromStore(
1964 const char * a_pInputData,
1965 size_t a_uInputDataLen,
1966 UChar * a_pOutputData,
1967 size_t a_uOutputDataSize)
1969 UErrorCode nError;
1971 if (!m_pConverter) {
1972 nError = U_ZERO_ERROR;
1973 m_pConverter = ucnv_open(m_pEncoding, &nError);
1974 if (U_FAILURE(nError)) {
1975 return false;
1979 nError = U_ZERO_ERROR;
1980 ucnv_resetToUnicode(m_pConverter);
1981 ucnv_toUChars(m_pConverter,
1982 a_pOutputData, (int32_t) a_uOutputDataSize,
1983 a_pInputData, (int32_t) a_uInputDataLen, &nError);
1984 if (U_FAILURE(nError)) {
1985 return false;
1988 return true;
1991 /** Calculate the number of char required by the storage format of this
1992 * data. The storage format is always UTF-8 or MBCS.
1994 * @param a_pInputData NULL terminated string to calculate the number of
1995 * bytes required to be converted to storage format.
1996 * @return Number of bytes required by the string when
1997 * converted to storage format. This size always
1998 * includes space for the terminating NULL character.
1999 * @return -1 cast to size_t on a conversion error.
2001 size_t SizeToStore(
2002 const UChar * a_pInputData)
2004 UErrorCode nError;
2006 if (!m_pConverter) {
2007 nError = U_ZERO_ERROR;
2008 m_pConverter = ucnv_open(m_pEncoding, &nError);
2009 if (U_FAILURE(nError)) {
2010 return (size_t) -1;
2014 nError = U_ZERO_ERROR;
2015 ucnv_resetFromUnicode(m_pConverter);
2016 int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,
2017 a_pInputData, -1, &nError);
2018 if (nError != U_BUFFER_OVERFLOW_ERROR) {
2019 return (size_t) -1;
2022 return (size_t) nLen + 1;
2025 /** Convert the input string to the storage format of this data.
2026 * The storage format is always UTF-8 or MBCS.
2028 * @param a_pInputData NULL terminated source string to convert. All of
2029 * the data will be converted including the
2030 * terminating NULL character.
2031 * @param a_pOutputData Pointer to the buffer to receive the converted
2032 * string.
2033 * @param a_pOutputDataSize Size of the output buffer in char.
2034 * @return true if all of the input data, including the
2035 * terminating NULL character was successfully
2036 * converted.
2038 bool ConvertToStore(
2039 const UChar * a_pInputData,
2040 char * a_pOutputData,
2041 size_t a_uOutputDataSize)
2043 UErrorCode nError;
2045 if (!m_pConverter) {
2046 nError = U_ZERO_ERROR;
2047 m_pConverter = ucnv_open(m_pEncoding, &nError);
2048 if (U_FAILURE(nError)) {
2049 return false;
2053 nError = U_ZERO_ERROR;
2054 ucnv_resetFromUnicode(m_pConverter);
2055 ucnv_fromUChars(m_pConverter,
2056 a_pOutputData, (int32_t) a_uOutputDataSize,
2057 a_pInputData, -1, &nError);
2058 if (U_FAILURE(nError)) {
2059 return false;
2062 return true;
2066 #endif // SI_CONVERT_ICU
2069 // ---------------------------------------------------------------------------
2070 // SI_CONVERT_WIN32
2071 // ---------------------------------------------------------------------------
2072 #ifdef SI_CONVERT_WIN32
2074 #define SI_Case SI_GenericCase
2076 // Windows CE doesn't have errno or MBCS libraries
2077 #ifdef _WIN32_WCE
2078 # ifndef SI_NO_MBCS
2079 # define SI_NO_MBCS
2080 # endif
2081 #endif
2083 #include <windows.h>
2084 #ifdef SI_NO_MBCS
2085 # define SI_NoCase SI_GenericNoCase
2086 #else // !SI_NO_MBCS
2088 * Case-insensitive comparison class using Win32 MBCS functions. This class
2089 * returns a case-insensitive semi-collation order for MBCS text. It may not
2090 * be safe for UTF-8 text returned in char format as we don't know what
2091 * characters will be folded by the function! Therefore, if you are using
2092 * SI_CHAR == char and SetUnicode(true), then you need to use the generic
2093 * SI_NoCase class instead.
2095 #include <mbstring.h>
2096 template<class SI_CHAR>
2097 struct SI_NoCase {
2098 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2099 if (sizeof(SI_CHAR) == sizeof(char)) {
2100 return _mbsicmp((const unsigned char *)pLeft,
2101 (const unsigned char *)pRight) < 0;
2103 if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
2104 return _wcsicmp((const wchar_t *)pLeft,
2105 (const wchar_t *)pRight) < 0;
2107 return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight);
2110 #endif // SI_NO_MBCS
2113 * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses
2114 * only the Win32 functions and doesn't require the external Unicode UTF-8
2115 * conversion library. It will not work on Windows 95 without using Microsoft
2116 * Layer for Unicode in your application.
2118 template<class SI_CHAR>
2119 class SI_ConvertW {
2120 UINT m_uCodePage;
2121 protected:
2122 SI_ConvertW() { }
2123 public:
2124 SI_ConvertW(bool a_bStoreIsUtf8) {
2125 m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP;
2128 /* copy and assignment */
2129 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
2130 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
2131 m_uCodePage = rhs.m_uCodePage;
2132 return *this;
2135 /** Calculate the number of SI_CHAR required for converting the input
2136 * from the storage format. The storage format is always UTF-8 or MBCS.
2138 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2139 * @param a_uInputDataLen Length of storage format data in bytes. This
2140 * must be the actual length of the data, including
2141 * NULL byte if NULL terminated string is required.
2142 * @return Number of SI_CHAR required by the string when
2143 * converted. If there are embedded NULL bytes in the
2144 * input data, only the string up and not including
2145 * the NULL byte will be converted.
2146 * @return -1 cast to size_t on a conversion error.
2148 size_t SizeFromStore(
2149 const char * a_pInputData,
2150 size_t a_uInputDataLen)
2152 assert(a_uInputDataLen != (size_t) -1);
2154 int retval = MultiByteToWideChar(
2155 m_uCodePage, 0,
2156 a_pInputData, (int) a_uInputDataLen,
2157 0, 0);
2158 return (size_t)(retval > 0 ? retval : -1);
2161 /** Convert the input string from the storage format to SI_CHAR.
2162 * The storage format is always UTF-8 or MBCS.
2164 * @param a_pInputData Data in storage format to be converted to SI_CHAR.
2165 * @param a_uInputDataLen Length of storage format data in bytes. This
2166 * must be the actual length of the data, including
2167 * NULL byte if NULL terminated string is required.
2168 * @param a_pOutputData Pointer to the output buffer to received the
2169 * converted data.
2170 * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2171 * @return true if all of the input data was successfully
2172 * converted.
2174 bool ConvertFromStore(
2175 const char * a_pInputData,
2176 size_t a_uInputDataLen,
2177 SI_CHAR * a_pOutputData,
2178 size_t a_uOutputDataSize)
2180 int nSize = MultiByteToWideChar(
2181 m_uCodePage, 0,
2182 a_pInputData, (int) a_uInputDataLen,
2183 (wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
2184 return (nSize > 0);
2187 /** Calculate the number of char required by the storage format of this
2188 * data. The storage format is always UTF-8.
2190 * @param a_pInputData NULL terminated string to calculate the number of
2191 * bytes required to be converted to storage format.
2192 * @return Number of bytes required by the string when
2193 * converted to storage format. This size always
2194 * includes space for the terminating NULL character.
2195 * @return -1 cast to size_t on a conversion error.
2197 size_t SizeToStore(
2198 const SI_CHAR * a_pInputData)
2200 int retval = WideCharToMultiByte(
2201 m_uCodePage, 0,
2202 (const wchar_t *) a_pInputData, -1,
2203 0, 0, 0, 0);
2204 return (size_t) (retval > 0 ? retval : -1);
2207 /** Convert the input string to the storage format of this data.
2208 * The storage format is always UTF-8 or MBCS.
2210 * @param a_pInputData NULL terminated source string to convert. All of
2211 * the data will be converted including the
2212 * terminating NULL character.
2213 * @param a_pOutputData Pointer to the buffer to receive the converted
2214 * string.
2215 * @param a_pOutputDataSize Size of the output buffer in char.
2216 * @return true if all of the input data, including the
2217 * terminating NULL character was successfully
2218 * converted.
2220 bool ConvertToStore(
2221 const SI_CHAR * a_pInputData,
2222 char * a_pOutputData,
2223 size_t a_uOutputDataSize)
2225 int retval = WideCharToMultiByte(
2226 m_uCodePage, 0,
2227 (const wchar_t *) a_pInputData, -1,
2228 a_pOutputData, (int) a_uOutputDataSize, 0, 0);
2229 return retval > 0;
2233 #endif // SI_CONVERT_WIN32
2236 // ---------------------------------------------------------------------------
2237 // TYPE DEFINITIONS
2238 // ---------------------------------------------------------------------------
2240 typedef CSimpleIniTempl<char,
2241 SI_NoCase<char>,SI_ConvertA<char> > CSimpleIniA;
2242 typedef CSimpleIniTempl<char,
2243 SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA;
2245 #if defined(SI_CONVERT_ICU)
2246 typedef CSimpleIniTempl<UChar,
2247 SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;
2248 typedef CSimpleIniTempl<UChar,
2249 SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;
2250 #else
2251 typedef CSimpleIniTempl<wchar_t,
2252 SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;
2253 typedef CSimpleIniTempl<wchar_t,
2254 SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;
2255 #endif
2257 #ifdef _UNICODE
2258 # define CSimpleIni CSimpleIniW
2259 # define CSimpleIniCase CSimpleIniCaseW
2260 # define SI_NEWLINE SI_NEWLINE_W
2261 #else // !_UNICODE
2262 # define CSimpleIni CSimpleIniA
2263 # define CSimpleIniCase CSimpleIniCaseA
2264 # define SI_NEWLINE SI_NEWLINE_A
2265 #endif // _UNICODE
2267 #endif // INCLUDED_SimpleIni_h