summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorerihel <erihel@gmail.com>2012-08-09 23:04:29 +0200
committererihel <erihel@gmail.com>2012-08-09 23:04:29 +0200
commit9946459c0cd65c3b66719a2aefc42c7ab2a29c04 (patch)
tree78e68a3a434082c5f42926f907adb05db9ae24dd /lib
parentf1d1cdceee3ec49546ba800a1b53a2dfb9c21e11 (diff)
downloadcolobot-9946459c0cd65c3b66719a2aefc42c7ab2a29c04.tar.gz
colobot-9946459c0cd65c3b66719a2aefc42c7ab2a29c04.tar.bz2
colobot-9946459c0cd65c3b66719a2aefc42c7ab2a29c04.zip
* changed 0, NULL to nullptr
* changed profile.cpp to use SimpleIni to load config files * added new CProfile singleton class for loading config * added SimpleIni to lib/ dir * added config loading tests
Diffstat (limited to 'lib')
-rw-r--r--lib/simpleini/ConvertUTF.c539
-rw-r--r--lib/simpleini/ConvertUTF.h149
-rw-r--r--lib/simpleini/Makefile28
-rw-r--r--lib/simpleini/SimpleIni.h3370
-rw-r--r--lib/simpleini/SimpleIni.sln29
-rw-r--r--lib/simpleini/SimpleIni.vcproj291
-rw-r--r--lib/simpleini/ini.syn36
-rw-r--r--lib/simpleini/package.cmd26
-rw-r--r--lib/simpleini/simpleini.doxy1321
-rw-r--r--lib/simpleini/simpleini.dsp178
-rw-r--r--lib/simpleini/simpleini.dsw29
-rw-r--r--lib/simpleini/snippets.cpp123
-rw-r--r--lib/simpleini/test.cmd24
-rw-r--r--lib/simpleini/test1-expected.ini85
-rw-r--r--lib/simpleini/test1-input.ini76
-rw-r--r--lib/simpleini/test1.cpp166
-rw-r--r--lib/simpleini/testsi-EUCJP.ini52
-rw-r--r--lib/simpleini/testsi-SJIS.ini51
-rw-r--r--lib/simpleini/testsi-UTF8.ini50
-rw-r--r--lib/simpleini/testsi.cpp309
20 files changed, 6932 insertions, 0 deletions
diff --git a/lib/simpleini/ConvertUTF.c b/lib/simpleini/ConvertUTF.c
new file mode 100644
index 0000000..4351b15
--- /dev/null
+++ b/lib/simpleini/ConvertUTF.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Source code file.
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Sept 2001: fixed const & error conditions per
+ mods suggested by S. Parent & A. Lillich.
+ June 2002: Tim Dodd added detection and handling of incomplete
+ source sequences, enhanced error detection, added casts
+ to eliminate compiler warnings.
+ July 2003: slight mods to back out aggressive FFFE detection.
+ Jan 2004: updated switches in from-UTF8 conversions.
+ Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
+
+ See the header file "ConvertUTF.h" for complete documentation.
+
+------------------------------------------------------------------------ */
+
+
+#include "ConvertUTF.h"
+#ifdef CVTUTF_DEBUG
+#include <stdio.h>
+#endif
+
+static const int halfShift = 10; /* used for shifting by 10 bits */
+
+static const UTF32 halfBase = 0x0010000UL;
+static const UTF32 halfMask = 0x3FFUL;
+
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+#define false 0
+#define true 1
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF16 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF32* source = *sourceStart;
+ UTF16* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ if (target >= targetEnd) {
+ result = targetExhausted; break;
+ }
+ ch = *source++;
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_LEGAL_UTF32) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ --source; /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF32 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16* source = *sourceStart;
+ UTF32* target = *targetStart;
+ UTF32 ch, ch2;
+ while (source < sourceEnd) {
+ const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd) {
+ ch2 = *source;
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ } else if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ if (target >= targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ *target++ = ch;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+#ifdef CVTUTF_DEBUG
+if (result == sourceIllegal) {
+ fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
+ fflush(stderr);
+}
+#endif
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+
+/*
+ * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+ * into the first byte, depending on how many bytes follow. There are
+ * as many entries in this table as there are UTF-8 sequence types.
+ * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
+ * for *legal* UTF-8 will be 4 or fewer bytes total.
+ */
+static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+/* --------------------------------------------------------------------- */
+
+/* The interface converts a whole buffer to avoid function-call overhead.
+ * Constants have been gathered. Loops & conditionals have been removed as
+ * much as possible for efficiency, in favor of drop-through switches.
+ * (See "Note A" at the bottom of the file for equivalent code.)
+ * If your compiler supports it, the "isLegalUTF8" call can be turned
+ * into an inline function.
+ */
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF8 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16* source = *sourceStart;
+ UTF8* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd) {
+ UTF32 ch2 = *source;
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ } else if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ /* Figure out how many bytes the result will require */
+ if (ch < (UTF32)0x80) { bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
+ } else if (ch < (UTF32)0x110000) { bytesToWrite = 4;
+ } else { bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ target -= bytesToWrite; result = targetExhausted; break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns false. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+
+static Boolean isLegalUTF8(const UTF8 *source, int length) {
+ UTF8 a;
+ const UTF8 *srcptr = source+length;
+ switch (length) {
+ default: return false;
+ /* Everything else falls through when "true"... */
+ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 2: if ((a = (*--srcptr)) > 0xBF) return false;
+
+ switch (*source) {
+ /* no fall-through in this inner switch */
+ case 0xE0: if (a < 0xA0) return false; break;
+ case 0xED: if (a > 0x9F) return false; break;
+ case 0xF0: if (a < 0x90) return false; break;
+ case 0xF4: if (a > 0x8F) return false; break;
+ default: if (a < 0x80) return false;
+ }
+
+ case 1: if (*source >= 0x80 && *source < 0xC2) return false;
+ }
+ if (*source > 0xF4) return false;
+ return true;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Exported function to return whether a UTF-8 sequence is legal or not.
+ * This is not used here; it's just exported.
+ */
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
+ int length = trailingBytesForUTF8[*source]+1;
+ if (source+length > sourceEnd) {
+ return false;
+ }
+ return isLegalUTF8(source, length);
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF16 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8* source = *sourceStart;
+ UTF16* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ result = sourceExhausted; break;
+ }
+ /* Do this check whether lenient or strict */
+ if (! isLegalUTF8(source, extraBytesToRead+1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (target >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_UTF16) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ source -= (extraBytesToRead+1); /* return to the start */
+ break; /* Bail out; shouldn't continue */
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF8 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF32* source = *sourceStart;
+ UTF8* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ ch = *source++;
+ if (flags == strictConversion ) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ /*
+ * Figure out how many bytes the result will require. Turn any
+ * illegally large UTF32 things (> Plane 17) into replacement chars.
+ */
+ if (ch < (UTF32)0x80) { bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
+ } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4;
+ } else { bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ result = sourceIllegal;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ --source; /* Back up source pointer! */
+ target -= bytesToWrite; result = targetExhausted; break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
+ case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF32 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8* source = *sourceStart;
+ UTF32* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ result = sourceExhausted; break;
+ }
+ /* Do this check whether lenient or strict */
+ if (! isLegalUTF8(source, extraBytesToRead+1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5: ch += *source++; ch <<= 6;
+ case 4: ch += *source++; ch <<= 6;
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (target >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up the source pointer! */
+ result = targetExhausted; break;
+ }
+ if (ch <= UNI_MAX_LEGAL_UTF32) {
+ /*
+ * UTF-16 surrogate values are illegal in UTF-32, and anything
+ * over Plane 17 (> 0x10FFFF) is illegal.
+ */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = ch;
+ }
+ } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
+ result = sourceIllegal;
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* ---------------------------------------------------------------------
+
+ Note A.
+ The fall-through switches in UTF-8 reading code save a
+ temp variable, some decrements & conditionals. The switches
+ are equivalent to the following loop:
+ {
+ int tmpBytesToRead = extraBytesToRead+1;
+ do {
+ ch += *source++;
+ --tmpBytesToRead;
+ if (tmpBytesToRead) ch <<= 6;
+ } while (tmpBytesToRead > 0);
+ }
+ In UTF-8 writing code, the switches on "bytesToWrite" are
+ similarly unrolled loops.
+
+ --------------------------------------------------------------------- */
diff --git a/lib/simpleini/ConvertUTF.h b/lib/simpleini/ConvertUTF.h
new file mode 100644
index 0000000..f1230ee
--- /dev/null
+++ b/lib/simpleini/ConvertUTF.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Header file.
+
+ Several funtions are included here, forming a complete set of
+ conversions between the three formats. UTF-7 is not included
+ here, but is handled in a separate source file.
+
+ Each of these routines takes pointers to input buffers and output
+ buffers. The input buffers are const.
+
+ Each routine converts the text between *sourceStart and sourceEnd,
+ putting the result into the buffer between *targetStart and
+ targetEnd. Note: the end pointers are *after* the last item: e.g.
+ *(sourceEnd - 1) is the last item.
+
+ The return result indicates whether the conversion was successful,
+ and if not, whether the problem was in the source or target buffers.
+ (Only the first encountered problem is indicated.)
+
+ After the conversion, *sourceStart and *targetStart are both
+ updated to point to the end of last text successfully converted in
+ the respective buffers.
+
+ Input parameters:
+ sourceStart - pointer to a pointer to the source buffer.
+ The contents of this are modified on return so that
+ it points at the next thing to be converted.
+ targetStart - similarly, pointer to pointer to the target buffer.
+ sourceEnd, targetEnd - respectively pointers to the ends of the
+ two buffers, for overflow checking only.
+
+ These conversion functions take a ConversionFlags argument. When this
+ flag is set to strict, both irregular sequences and isolated surrogates
+ will cause an error. When the flag is set to lenient, both irregular
+ sequences and isolated surrogates are converted.
+
+ Whether the flag is strict or lenient, all illegal sequences will cause
+ an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
+ or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
+ must check for illegal sequences.
+
+ When the flag is set to lenient, characters over 0x10FFFF are converted
+ to the replacement character; otherwise (when the flag is set to strict)
+ they constitute an error.
+
+ Output parameters:
+ The value "sourceIllegal" is returned from some routines if the input
+ sequence is malformed. When "sourceIllegal" is returned, the source
+ value will point to the illegal value that caused the problem. E.g.,
+ in UTF-8 when a sequence is malformed, it points to the start of the
+ malformed sequence.
+
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Fixes & updates, Sept 2001.
+
+------------------------------------------------------------------------ */
+
+/* ---------------------------------------------------------------------
+ The following 4 definitions are compiler-specific.
+ The C standard does not guarantee that wchar_t has at least
+ 16 bits, so wchar_t is no less portable than unsigned short!
+ All should be unsigned values to avoid sign extension during
+ bit mask & shift operations.
+------------------------------------------------------------------------ */
+
+typedef unsigned int UTF32; /* at least 32 bits */
+typedef unsigned short UTF16; /* at least 16 bits */
+typedef unsigned char UTF8; /* typically 8 bits */
+typedef unsigned char Boolean; /* 0 or 1 */
+
+/* Some fundamental constants */
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_MAX_BMP (UTF32)0x0000FFFF
+#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
+#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
+#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
+
+typedef enum {
+ conversionOK, /* conversion successful */
+ sourceExhausted, /* partial character in source, but hit end */
+ targetExhausted, /* insuff. room in target for conversion */
+ sourceIllegal /* source sequence is illegal/malformed */
+} ConversionResult;
+
+typedef enum {
+ strictConversion = 0,
+ lenientConversion
+} ConversionFlags;
+
+/* This is for C++ and does no harm in C */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ConversionResult ConvertUTF8toUTF16 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF16toUTF8 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF8toUTF32 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF32toUTF8 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF16toUTF32 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF32toUTF16 (
+ const UTF32** sourceStart, const UTF32* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
+
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* --------------------------------------------------------------------- */
diff --git a/lib/simpleini/Makefile b/lib/simpleini/Makefile
new file mode 100644
index 0000000..a04b5e3
--- /dev/null
+++ b/lib/simpleini/Makefile
@@ -0,0 +1,28 @@
+CC=g++
+CFLAGS=-Wall
+CPPFLAGS=-Wall
+
+OBJS=testsi.o test1.o snippets.o ConvertUTF.o
+
+help:
+ @echo This makefile is just for the test program \(use \"make clean all test\"\)
+ @echo Just include the SimpleIni.h header file to use it.
+
+all: $(OBJS)
+ $(CC) -o testsi $(OBJS)
+
+clean:
+ rm -f core *.o testsi
+
+data:
+ sed 's/\r\n$$/\n/g' < test1-expected.ini > unix.out
+ mv unix.out test1-expected.ini
+
+test: testsi
+ ./testsi -u -m -l test1-input.ini > test1-blah.ini
+ diff test1-output.ini test1-expected.ini
+
+install:
+ @echo No install required. Just include the SimpleIni.h header file to use it.
+
+testsi.o test1.o snippets.o : SimpleIni.h
diff --git a/lib/simpleini/SimpleIni.h b/lib/simpleini/SimpleIni.h
new file mode 100644
index 0000000..f9f36bb
--- /dev/null
+++ b/lib/simpleini/SimpleIni.h
@@ -0,0 +1,3370 @@
+/** @mainpage
+
+ <table>
+ <tr><th>Library <td>SimpleIni
+ <tr><th>File <td>SimpleIni.h
+ <tr><th>Author <td>Brodie Thiesfield [code at jellycan dot com]
+ <tr><th>Source <td>http://code.jellycan.com/simpleini/
+ <tr><th>Version <td>4.15
+ </table>
+
+ Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation.
+
+ @section intro INTRODUCTION
+
+ This component allows an INI-style configuration file to be used on both
+ Windows and Linux/Unix. It is fast, simple and source code using this
+ component will compile unchanged on either OS.
+
+
+ @section features FEATURES
+
+ - MIT Licence allows free use in all software (including GPL and commercial)
+ - multi-platform (Windows 95/98/ME/NT/2K/XP/2003, Windows CE, Linux, Unix)
+ - loading and saving of INI-style configuration files
+ - configuration files can have any newline format on all platforms
+ - liberal acceptance of file format
+ - key/values with no section
+ - removal of whitespace around sections, keys and values
+ - support for multi-line values (values with embedded newline characters)
+ - optional support for multiple keys with the same name
+ - optional case-insensitive sections and keys (for ASCII characters only)
+ - saves files with sections and keys in the same order as they were loaded
+ - preserves comments on the file, section and keys where possible.
+ - supports both char or wchar_t programming interfaces
+ - supports both MBCS (system locale) and UTF-8 file encodings
+ - system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file
+ - support for non-ASCII characters in section, keys, values and comments
+ - support for non-standard character types or file encodings
+ via user-written converter classes
+ - support for adding/modifying values programmatically
+ - compiles cleanly in the following compilers:
+ - Windows/VC6 (warning level 3)
+ - Windows/VC.NET 2003 (warning level 4)
+ - Windows/VC 2005 (warning level 4)
+ - Linux/gcc (-Wall)
+
+
+ @section usage USAGE SUMMARY
+
+ -# Define the appropriate symbol for the converter you wish to use and
+ include the SimpleIni.h header file. If no specific converter is defined
+ then the default converter is used. The default conversion mode uses
+ SI_CONVERT_WIN32 on Windows and SI_CONVERT_GENERIC on all other
+ platforms. If you are using ICU then SI_CONVERT_ICU is supported on all
+ platforms.
+ -# Declare an instance the appropriate class. Note that the following
+ definitions are just shortcuts for commonly used types. Other types
+ (PRUnichar, unsigned short, unsigned char) are also possible.
+ <table>
+ <tr><th>Interface <th>Case-sensitive <th>Load UTF-8 <th>Load MBCS <th>Typedef
+ <tr><th>SI_CONVERT_GENERIC
+ <tr><td>char <td>No <td>Yes <td>Yes #1 <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
+ <tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW
+ <tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
+ <tr><th>SI_CONVERT_WIN32
+ <tr><td>char <td>No <td>No #2 <td>Yes <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
+ <tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW
+ <tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
+ <tr><th>SI_CONVERT_ICU
+ <tr><td>char <td>No <td>Yes <td>Yes <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
+ <tr><td>UChar <td>No <td>Yes <td>Yes <td>CSimpleIniW
+ <tr><td>UChar <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
+ </table>
+ #1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.<br>
+ #2 Only affects Windows. On Windows this uses MBCS functions and
+ so may fold case incorrectly leading to uncertain results.
+ -# Call LoadData() or LoadFile() to load and parse the INI configuration file
+ -# Access and modify the data of the file using the following functions
+ <table>
+ <tr><td>GetAllSections <td>Return all section names
+ <tr><td>GetAllKeys <td>Return all key names within a section
+ <tr><td>GetAllValues <td>Return all values within a section & key
+ <tr><td>GetSection <td>Return all key names and values in a section
+ <tr><td>GetSectionSize <td>Return the number of keys in a section
+ <tr><td>GetValue <td>Return a value for a section & key
+ <tr><td>SetValue <td>Add or update a value for a section & key
+ <tr><td>Delete <td>Remove a section, or a key from a section
+ </table>
+ -# Call Save() or SaveFile() to save the INI configuration data
+
+ @section iostreams IO STREAMS
+
+ SimpleIni supports reading from and writing to STL IO streams. Enable this
+ by defining SI_SUPPORT_IOSTREAMS before including the SimpleIni.h header
+ file. Ensure that if the streams are backed by a file (e.g. ifstream or
+ ofstream) then the flag ios_base::binary has been used when the file was
+ opened.
+
+ @section multiline MULTI-LINE VALUES
+
+ Values that span multiple lines are created using the following format.
+
+ <pre>
+ key = <<<ENDTAG
+ .... multiline value ....
+ ENDTAG
+ </pre>
+
+ Note the following:
+ - The text used for ENDTAG can be anything and is used to find
+ where the multi-line text ends.
+ - The newline after ENDTAG in the start tag, and the newline
+ before ENDTAG in the end tag is not included in the data value.
+ - The ending tag must be on it's own line with no whitespace before
+ or after it.
+ - The multi-line value is modified at load so that each line in the value
+ is delimited by a single '\\n' character on all platforms. At save time
+ it will be converted into the newline format used by the current
+ platform.
+
+ @section comments COMMENTS
+
+ Comments are preserved in the file within the following restrictions:
+ - Every file may have a single "file comment". It must start with the
+ first character in the file, and will end with the first non-comment
+ line in the file.
+ - Every section may have a single "section comment". It will start
+ with the first comment line following the file comment, or the last
+ data entry. It ends at the beginning of the section.
+ - Every key may have a single "key comment". This comment will start
+ with the first comment line following the section start, or the file
+ comment if there is no section name.
+ - Comments are set at the time that the file, section or key is first
+ created. The only way to modify a comment on a section or a key is to
+ delete that entry and recreate it with the new comment. There is no
+ way to change the file comment.
+
+ @section save SAVE ORDER
+
+ The sections and keys are written out in the same order as they were
+ read in from the file. Sections and keys added to the data after the
+ file has been loaded will be added to the end of the file when it is
+ written. There is no way to specify the location of a section or key
+ other than in first-created, first-saved order.
+
+ @section notes NOTES
+
+ - To load UTF-8 data on Windows 95, you need to use Microsoft Layer for
+ Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU.
+ - When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked.
+ - When using SI_CONVERT_ICU, ICU header files must be on the include
+ path and icuuc.lib must be linked in.
+ - To load a UTF-8 file on Windows AND expose it with SI_CHAR == char,
+ you should use SI_CONVERT_GENERIC.
+ - The collation (sorting) order used for sections and keys returned from
+ iterators is NOT DEFINED. If collation order of the text is important
+ then it should be done yourself by either supplying a replacement
+ SI_STRLESS class, or by sorting the strings external to this library.
+ - Usage of the <mbstring.h> header on Windows can be disabled by defining
+ SI_NO_MBCS. This is defined automatically on Windows CE platforms.
+
+ @section contrib CONTRIBUTIONS
+
+ - 2010/05/03: Tobias Gehrig: added GetDoubleValue()
+
+ @section licence MIT LICENCE
+
+ The licence text below is the boilerplate "MIT Licence" used from:
+ http://www.opensource.org/licenses/mit-license.php
+
+ Copyright (c) 2006-2012, Brodie Thiesfield
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is furnished
+ to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef INCLUDED_SimpleIni_h
+#define INCLUDED_SimpleIni_h
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+// Disable these warnings in MSVC:
+// 4127 "conditional expression is constant" as the conversion classes trigger
+// it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
+// be optimized away in a release build.
+// 4503 'insert' : decorated name length exceeded, name was truncated
+// 4702 "unreachable code" as the MS STL header causes it in release mode.
+// Again, the code causing the warning will be cleaned up by the compiler.
+// 4786 "identifier truncated to 256 characters" as this is thrown hundreds
+// of times VC6 as soon as STL is used.
+#ifdef _MSC_VER
+# pragma warning (push)
+# pragma warning (disable: 4127 4503 4702 4786)
+#endif
+
+#include <cstring>
+#include <string>
+#include <map>
+#include <list>
+#include <algorithm>
+#include <stdio.h>
+
+#ifdef SI_SUPPORT_IOSTREAMS
+# include <iostream>
+#endif // SI_SUPPORT_IOSTREAMS
+
+#ifdef _DEBUG
+# ifndef assert
+# include <cassert>
+# endif
+# define SI_ASSERT(x) assert(x)
+#else
+# define SI_ASSERT(x)
+#endif
+
+enum SI_Error {
+ SI_OK = 0, //!< No error
+ SI_UPDATED = 1, //!< An existing value was updated
+ SI_INSERTED = 2, //!< A new value was inserted
+
+ // note: test for any error with (retval < 0)
+ SI_FAIL = -1, //!< Generic failure
+ SI_NOMEM = -2, //!< Out of memory error
+ SI_FILE = -3 //!< File error (see errno for detail error)
+};
+
+#define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
+
+#ifdef _WIN32
+# define SI_NEWLINE_A "\r\n"
+# define SI_NEWLINE_W L"\r\n"
+#else // !_WIN32
+# define SI_NEWLINE_A "\n"
+# define SI_NEWLINE_W L"\n"
+#endif // _WIN32
+
+#if defined(SI_CONVERT_ICU)
+# include <unicode/ustring.h>
+#endif
+
+#if defined(_WIN32)
+# define SI_HAS_WIDE_FILE
+# define SI_WCHAR_T wchar_t
+#elif defined(SI_CONVERT_ICU)
+# define SI_HAS_WIDE_FILE
+# define SI_WCHAR_T UChar
+#endif
+
+
+// ---------------------------------------------------------------------------
+// MAIN TEMPLATE CLASS
+// ---------------------------------------------------------------------------
+
+/** Simple INI file reader.
+
+ This can be instantiated with the choice of unicode or native characterset,
+ and case sensitive or insensitive comparisons of section and key names.
+ The supported combinations are pre-defined with the following typedefs:
+
+ <table>
+ <tr><th>Interface <th>Case-sensitive <th>Typedef
+ <tr><td>char <td>No <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>CSimpleIniCaseA
+ <tr><td>wchar_t <td>No <td>CSimpleIniW
+ <tr><td>wchar_t <td>Yes <td>CSimpleIniCaseW
+ </table>
+
+ Note that using other types for the SI_CHAR is supported. For instance,
+ unsigned char, unsigned short, etc. Note that where the alternative type
+ is a different size to char/wchar_t you may need to supply new helper
+ classes for SI_STRLESS and SI_CONVERTER.
+ */
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+class CSimpleIniTempl
+{
+public:
+ /** key entry */
+ struct Entry {
+ const SI_CHAR * pItem;
+ const SI_CHAR * pComment;
+ int nOrder;
+
+ Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0)
+ : pItem(a_pszItem)
+ , pComment(NULL)
+ , nOrder(a_nOrder)
+ { }
+ Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder)
+ : pItem(a_pszItem)
+ , pComment(a_pszComment)
+ , nOrder(a_nOrder)
+ { }
+ Entry(const Entry & rhs) { operator=(rhs); }
+ Entry & operator=(const Entry & rhs) {
+ pItem = rhs.pItem;
+ pComment = rhs.pComment;
+ nOrder = rhs.nOrder;
+ return *this;
+ }
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+ /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */
+ bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); }
+ bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); }
+#endif
+
+ /** Strict less ordering by name of key only */
+ struct KeyOrder : std::binary_function<Entry, Entry, bool> {
+ bool operator()(const Entry & lhs, const Entry & rhs) const {
+ const static SI_STRLESS isLess = SI_STRLESS();
+ return isLess(lhs.pItem, rhs.pItem);
+ }
+ };
+
+ /** Strict less ordering by order, and then name of key */
+ struct LoadOrder : std::binary_function<Entry, Entry, bool> {
+ bool operator()(const Entry & lhs, const Entry & rhs) const {
+ if (lhs.nOrder != rhs.nOrder) {
+ return lhs.nOrder < rhs.nOrder;
+ }
+ return KeyOrder()(lhs.pItem, rhs.pItem);
+ }
+ };
+ };
+
+ /** map keys to values */
+ typedef std::multimap<Entry,const SI_CHAR *,typename Entry::KeyOrder> TKeyVal;
+
+ /** map sections to key/value map */
+ typedef std::map<Entry,TKeyVal,typename Entry::KeyOrder> TSection;
+
+ /** set of dependent string pointers. Note that these pointers are
+ dependent on memory owned by CSimpleIni.
+ */
+ typedef std::list<Entry> TNamesDepend;
+
+ /** interface definition for the OutputWriter object to pass to Save()
+ in order to output the INI file data.
+ */
+ class OutputWriter {
+ public:
+ OutputWriter() { }
+ virtual ~OutputWriter() { }
+ virtual void Write(const char * a_pBuf) = 0;
+ private:
+ OutputWriter(const OutputWriter &); // disable
+ OutputWriter & operator=(const OutputWriter &); // disable
+ };
+
+ /** OutputWriter class to write the INI data to a file */
+ class FileWriter : public OutputWriter {
+ FILE * m_file;
+ public:
+ FileWriter(FILE * a_file) : m_file(a_file) { }
+ void Write(const char * a_pBuf) {
+ fputs(a_pBuf, m_file);
+ }
+ private:
+ FileWriter(const FileWriter &); // disable
+ FileWriter & operator=(const FileWriter &); // disable
+ };
+
+ /** OutputWriter class to write the INI data to a string */
+ class StringWriter : public OutputWriter {
+ std::string & m_string;
+ public:
+ StringWriter(std::string & a_string) : m_string(a_string) { }
+ void Write(const char * a_pBuf) {
+ m_string.append(a_pBuf);
+ }
+ private:
+ StringWriter(const StringWriter &); // disable
+ StringWriter & operator=(const StringWriter &); // disable
+ };
+
+#ifdef SI_SUPPORT_IOSTREAMS
+ /** OutputWriter class to write the INI data to an ostream */
+ class StreamWriter : public OutputWriter {
+ std::ostream & m_ostream;
+ public:
+ StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { }
+ void Write(const char * a_pBuf) {
+ m_ostream << a_pBuf;
+ }
+ private:
+ StreamWriter(const StreamWriter &); // disable
+ StreamWriter & operator=(const StreamWriter &); // disable
+ };
+#endif // SI_SUPPORT_IOSTREAMS
+
+ /** Characterset conversion utility class to convert strings to the
+ same format as is used for the storage.
+ */
+ class Converter : private SI_CONVERTER {
+ public:
+ Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) {
+ m_scratch.resize(1024);
+ }
+ Converter(const Converter & rhs) { operator=(rhs); }
+ Converter & operator=(const Converter & rhs) {
+ m_scratch = rhs.m_scratch;
+ return *this;
+ }
+ bool ConvertToStore(const SI_CHAR * a_pszString) {
+ size_t uLen = SizeToStore(a_pszString);
+ if (uLen == (size_t)(-1)) {
+ return false;
+ }
+ while (uLen > m_scratch.size()) {
+ m_scratch.resize(m_scratch.size() * 2);
+ }
+ return SI_CONVERTER::ConvertToStore(
+ a_pszString,
+ const_cast<char*>(m_scratch.data()),
+ m_scratch.size());
+ }
+ const char * Data() { return m_scratch.data(); }
+ private:
+ std::string m_scratch;
+ };
+
+public:
+ /*-----------------------------------------------------------------------*/
+
+ /** Default constructor.
+
+ @param a_bIsUtf8 See the method SetUnicode() for details.
+ @param a_bMultiKey See the method SetMultiKey() for details.
+ @param a_bMultiLine See the method SetMultiLine() for details.
+ */
+ CSimpleIniTempl(
+ bool a_bIsUtf8 = false,
+ bool a_bMultiKey = false,
+ bool a_bMultiLine = false
+ );
+
+ /** Destructor */
+ ~CSimpleIniTempl();
+
+ /** Deallocate all memory stored by this object */
+ void Reset();
+
+ /** Has any data been loaded */
+ bool IsEmpty() const { return m_data.empty(); }
+
+ /*-----------------------------------------------------------------------*/
+ /** @{ @name Settings */
+
+ /** Set the storage format of the INI data. This affects both the loading
+ and saving of the INI data using all of the Load/Save API functions.
+ This value cannot be changed after any INI data has been loaded.
+
+ If the file is not set to Unicode (UTF-8), then the data encoding is
+ assumed to be the OS native encoding. This encoding is the system
+ locale on Linux/Unix and the legacy MBCS encoding on Windows NT/2K/XP.
+ If the storage format is set to Unicode then the file will be loaded
+ as UTF-8 encoded data regardless of the native file encoding. If
+ SI_CHAR == char then all of the char* parameters take and return UTF-8
+ encoded data regardless of the system locale.
+
+ \param a_bIsUtf8 Assume UTF-8 encoding for the source?
+ */
+ void SetUnicode(bool a_bIsUtf8 = true) {
+ if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8;
+ }
+
+ /** Get the storage format of the INI data. */
+ bool IsUnicode() const { return m_bStoreIsUtf8; }
+
+ /** Should multiple identical keys be permitted in the file. If set to false
+ then the last value encountered will be used as the value of the key.
+ If set to true, then all values will be available to be queried. For
+ example, with the following input:
+
+ <pre>
+ [section]
+ test=value1
+ test=value2
+ </pre>
+
+ Then with SetMultiKey(true), both of the values "value1" and "value2"
+ will be returned for the key test. If SetMultiKey(false) is used, then
+ the value for "test" will only be "value2". This value may be changed
+ at any time.
+
+ \param a_bAllowMultiKey Allow multi-keys in the source?
+ */
+ void SetMultiKey(bool a_bAllowMultiKey = true) {
+ m_bAllowMultiKey = a_bAllowMultiKey;
+ }
+
+ /** Get the storage format of the INI data. */
+ bool IsMultiKey() const { return m_bAllowMultiKey; }
+
+ /** Should data values be permitted to span multiple lines in the file. If
+ set to false then the multi-line construct <<<TAG as a value will be
+ returned as is instead of loading the data. This value may be changed
+ at any time.
+
+ \param a_bAllowMultiLine Allow multi-line values in the source?
+ */
+ void SetMultiLine(bool a_bAllowMultiLine = true) {
+ m_bAllowMultiLine = a_bAllowMultiLine;
+ }
+
+ /** Query the status of multi-line data */
+ bool IsMultiLine() const { return m_bAllowMultiLine; }
+
+ /** Should spaces be added around the equals sign when writing key/value
+ pairs out. When true, the result will be "key = value". When false,
+ the result will be "key=value". This value may be changed at any time.
+
+ \param a_bSpaces Add spaces around the equals sign?
+ */
+ void SetSpaces(bool a_bSpaces = true) {
+ m_bSpaces = a_bSpaces;
+ }
+
+ /** Query the status of spaces output */
+ bool UsingSpaces() const { return m_bSpaces; }
+
+ /*-----------------------------------------------------------------------*/
+ /** @}
+ @{ @name Loading INI Data */
+
+ /** Load an INI file from disk into memory
+
+ @param a_pszFile Path of the file to be loaded. This will be passed
+ to fopen() and so must be a valid path for the
+ current platform.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadFile(
+ const char * a_pszFile
+ );
+
+#ifdef SI_HAS_WIDE_FILE
+ /** Load an INI file from disk into memory
+
+ @param a_pwszFile Path of the file to be loaded in UTF-16.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadFile(
+ const SI_WCHAR_T * a_pwszFile
+ );
+#endif // SI_HAS_WIDE_FILE
+
+ /** Load the file from a file pointer.
+
+ @param a_fpFile Valid file pointer to read the file data from. The
+ file will be read until end of file.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadFile(
+ FILE * a_fpFile
+ );
+
+#ifdef SI_SUPPORT_IOSTREAMS
+ /** Load INI file data from an istream.
+
+ @param a_istream Stream to read from
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadData(
+ std::istream & a_istream
+ );
+#endif // SI_SUPPORT_IOSTREAMS
+
+ /** Load INI file data direct from a std::string
+
+ @param a_strData Data to be loaded
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadData(const std::string & a_strData) {
+ return LoadData(a_strData.c_str(), a_strData.size());
+ }
+
+ /** Load INI file data direct from memory
+
+ @param a_pData Data to be loaded
+ @param a_uDataLen Length of the data in bytes
+
+ @return SI_Error See error definitions
+ */
+ SI_Error LoadData(
+ const char * a_pData,
+ size_t a_uDataLen
+ );
+
+ /*-----------------------------------------------------------------------*/
+ /** @}
+ @{ @name Saving INI Data */
+
+ /** Save an INI file from memory to disk
+
+ @param a_pszFile Path of the file to be saved. This will be passed
+ to fopen() and so must be a valid path for the
+ current platform.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is
+ in UTF-8 format. If it is not UTF-8 then
+ this parameter is ignored.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error SaveFile(
+ const char * a_pszFile,
+ bool a_bAddSignature = true
+ ) const;
+
+#ifdef SI_HAS_WIDE_FILE
+ /** Save an INI file from memory to disk
+
+ @param a_pwszFile Path of the file to be saved in UTF-16.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is
+ in UTF-8 format. If it is not UTF-8 then
+ this parameter is ignored.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error SaveFile(
+ const SI_WCHAR_T * a_pwszFile,
+ bool a_bAddSignature = true
+ ) const;
+#endif // _WIN32
+
+ /** Save the INI data to a file. See Save() for details.
+
+ @param a_pFile Handle to a file. File should be opened for
+ binary output.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
+ UTF-8 format. If it is not UTF-8 then this value is
+ ignored. Do not set this to true if anything has
+ already been written to the file.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error SaveFile(
+ FILE * a_pFile,
+ bool a_bAddSignature = false
+ ) const;
+
+ /** Save the INI data. The data will be written to the output device
+ in a format appropriate to the current data, selected by:
+
+ <table>
+ <tr><th>SI_CHAR <th>FORMAT
+ <tr><td>char <td>same format as when loaded (MBCS or UTF-8)
+ <tr><td>wchar_t <td>UTF-8
+ <tr><td>other <td>UTF-8
+ </table>
+
+ Note that comments from the original data is preserved as per the
+ documentation on comments. The order of the sections and values
+ from the original file will be preserved.
+
+ Any data prepended or appended to the output device must use the the
+ same format (MBCS or UTF-8). You may use the GetConverter() method to
+ convert text to the correct format regardless of the output format
+ being used by SimpleIni.
+
+ To add a BOM to UTF-8 data, write it out manually at the very beginning
+ like is done in SaveFile when a_bUseBOM is true.
+
+ @param a_oOutput Output writer to write the data to.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
+ UTF-8 format. If it is not UTF-8 then this value is
+ ignored. Do not set this to true if anything has
+ already been written to the OutputWriter.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error Save(
+ OutputWriter & a_oOutput,
+ bool a_bAddSignature = false
+ ) const;
+
+#ifdef SI_SUPPORT_IOSTREAMS
+ /** Save the INI data to an ostream. See Save() for details.
+
+ @param a_ostream String to have the INI data appended to.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
+ UTF-8 format. If it is not UTF-8 then this value is
+ ignored. Do not set this to true if anything has
+ already been written to the stream.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error Save(
+ std::ostream & a_ostream,
+ bool a_bAddSignature = false
+ ) const
+ {
+ StreamWriter writer(a_ostream);
+ return Save(writer, a_bAddSignature);
+ }
+#endif // SI_SUPPORT_IOSTREAMS
+
+ /** Append the INI data to a string. See Save() for details.
+
+ @param a_sBuffer String to have the INI data appended to.
+
+ @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
+ UTF-8 format. If it is not UTF-8 then this value is
+ ignored. Do not set this to true if anything has
+ already been written to the string.
+
+ @return SI_Error See error definitions
+ */
+ SI_Error Save(
+ std::string & a_sBuffer,
+ bool a_bAddSignature = false
+ ) const
+ {
+ StringWriter writer(a_sBuffer);
+ return Save(writer, a_bAddSignature);
+ }
+
+ /*-----------------------------------------------------------------------*/
+ /** @}
+ @{ @name Accessing INI Data */
+
+ /** Retrieve all section names. The list is returned as an STL vector of
+ names and can be iterated or searched as necessary. Note that the
+ sort order of the returned strings is NOT DEFINED. You can sort
+ the names into the load order if desired. Search this file for ".sort"
+ for an example.
+
+ NOTE! This structure contains only pointers to strings. The actual
+ string data is stored in memory owned by CSimpleIni. Ensure that the
+ CSimpleIni object is not destroyed or Reset() while these pointers
+ are in use!
+
+ @param a_names Vector that will receive all of the section
+ names. See note above!
+ */
+ void GetAllSections(
+ TNamesDepend & a_names
+ ) const;
+
+ /** Retrieve all unique key names in a section. The sort order of the
+ returned strings is NOT DEFINED. You can sort the names into the load
+ order if desired. Search this file for ".sort" for an example. Only
+ unique key names are returned.
+
+ NOTE! This structure contains only pointers to strings. The actual
+ string data is stored in memory owned by CSimpleIni. Ensure that the
+ CSimpleIni object is not destroyed or Reset() while these strings
+ are in use!
+
+ @param a_pSection Section to request data for
+ @param a_names List that will receive all of the key
+ names. See note above!
+
+ @return true Section was found.
+ @return false Matching section was not found.
+ */
+ bool GetAllKeys(
+ const SI_CHAR * a_pSection,
+ TNamesDepend & a_names
+ ) const;
+
+ /** Retrieve all values for a specific key. This method can be used when
+ multiple keys are both enabled and disabled. Note that the sort order
+ of the returned strings is NOT DEFINED. You can sort the names into
+ the load order if desired. Search this file for ".sort" for an example.
+
+ NOTE! The returned values are pointers to string data stored in memory
+ owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
+ or Reset while you are using this pointer!
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_values List to return if the key is not found
+
+ @return true Key was found.
+ @return false Matching section/key was not found.
+ */
+ bool GetAllValues(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ TNamesDepend & a_values
+ ) const;
+
+ /** Query the number of keys in a specific section. Note that if multiple
+ keys are enabled, then this value may be different to the number of
+ keys returned by GetAllKeys.
+
+ @param a_pSection Section to request data for
+
+ @return -1 Section does not exist in the file
+ @return >=0 Number of keys in the section
+ */
+ int GetSectionSize(
+ const SI_CHAR * a_pSection
+ ) const;
+
+ /** Retrieve all key and value pairs for a section. The data is returned
+ as a pointer to an STL map and can be iterated or searched as
+ desired. Note that multiple entries for the same key may exist when
+ multiple keys have been enabled.
+
+ NOTE! This structure contains only pointers to strings. The actual
+ string data is stored in memory owned by CSimpleIni. Ensure that the
+ CSimpleIni object is not destroyed or Reset() while these strings
+ are in use!
+
+ @param a_pSection Name of the section to return
+ @return boolean Was a section matching the supplied
+ name found.
+ */
+ const TKeyVal * GetSection(
+ const SI_CHAR * a_pSection
+ ) const;
+
+ /** Retrieve the value for a specific key. If multiple keys are enabled
+ (see SetMultiKey) then only the first value associated with that key
+ will be returned, see GetAllValues for getting all values with multikey.
+
+ NOTE! The returned value is a pointer to string data stored in memory
+ owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
+ or Reset while you are using this pointer!
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_pDefault Value to return if the key is not found
+ @param a_pHasMultiple Optionally receive notification of if there are
+ multiple entries for this key.
+
+ @return a_pDefault Key was not found in the section
+ @return other Value of the key
+ */
+ const SI_CHAR * GetValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pDefault = NULL,
+ bool * a_pHasMultiple = NULL
+ ) const;
+
+ /** Retrieve a numeric value for a specific key. If multiple keys are enabled
+ (see SetMultiKey) then only the first value associated with that key
+ will be returned, see GetAllValues for getting all values with multikey.
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_nDefault Value to return if the key is not found
+ @param a_pHasMultiple Optionally receive notification of if there are
+ multiple entries for this key.
+
+ @return a_nDefault Key was not found in the section
+ @return other Value of the key
+ */
+ long GetLongValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ long a_nDefault = 0,
+ bool * a_pHasMultiple = NULL
+ ) const;
+
+ /** Retrieve a numeric value for a specific key. If multiple keys are enabled
+ (see SetMultiKey) then only the first value associated with that key
+ will be returned, see GetAllValues for getting all values with multikey.
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_nDefault Value to return if the key is not found
+ @param a_pHasMultiple Optionally receive notification of if there are
+ multiple entries for this key.
+
+ @return a_nDefault Key was not found in the section
+ @return other Value of the key
+ */
+ double GetDoubleValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ double a_nDefault = 0,
+ bool * a_pHasMultiple = NULL
+ ) const;
+
+ /** Retrieve a boolean value for a specific key. If multiple keys are enabled
+ (see SetMultiKey) then only the first value associated with that key
+ will be returned, see GetAllValues for getting all values with multikey.
+
+ Strings starting with "t", "y", "on" or "1" are returned as logically true.
+ Strings starting with "f", "n", "of" or "0" are returned as logically false.
+ For all other values the default is returned. Character comparisons are
+ case-insensitive.
+
+ @param a_pSection Section to search
+ @param a_pKey Key to search for
+ @param a_bDefault Value to return if the key is not found
+ @param a_pHasMultiple Optionally receive notification of if there are
+ multiple entries for this key.
+
+ @return a_nDefault Key was not found in the section
+ @return other Value of the key
+ */
+ bool GetBoolValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bDefault = false,
+ bool * a_pHasMultiple = NULL
+ ) const;
+
+ /** Add or update a section or value. This will always insert
+ when multiple keys are enabled.
+
+ @param a_pSection Section to add or update
+ @param a_pKey Key to add or update. Set to NULL to
+ create an empty section.
+ @param a_pValue Value to set. Set to NULL to create an
+ empty section.
+ @param a_pComment Comment to be associated with the section or the
+ key. If a_pKey is NULL then it will be associated
+ with the section, otherwise the key. Note that a
+ comment may be set ONLY when the section or key is
+ first created (i.e. when this function returns the
+ value SI_INSERTED). If you wish to create a section
+ with a comment then you need to create the section
+ separately to the key. The comment string must be
+ in full comment form already (have a comment
+ character starting every line).
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/SetValue and SetValue
+ with a_bForceReplace = true, is that the load
+ order and comment will be preserved this way.
+
+ @return SI_Error See error definitions
+ @return SI_UPDATED Value was updated
+ @return SI_INSERTED Value was inserted
+ */
+ SI_Error SetValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pValue,
+ const SI_CHAR * a_pComment = NULL,
+ bool a_bForceReplace = false
+ )
+ {
+ return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true);
+ }
+
+ /** Add or update a numeric value. This will always insert
+ when multiple keys are enabled.
+
+ @param a_pSection Section to add or update
+ @param a_pKey Key to add or update.
+ @param a_nValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
+ notes on SetValue() for comments.
+ @param a_bUseHex By default the value will be written to the file
+ in decimal format. Set this to true to write it
+ as hexadecimal.
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/SetLongValue and
+ SetLongValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
+ way.
+
+ @return SI_Error See error definitions
+ @return SI_UPDATED Value was updated
+ @return SI_INSERTED Value was inserted
+ */
+ SI_Error SetLongValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ long a_nValue,
+ const SI_CHAR * a_pComment = NULL,
+ bool a_bUseHex = false,
+ bool a_bForceReplace = false
+ );
+
+ /** Add or update a double value. This will always insert
+ when multiple keys are enabled.
+
+ @param a_pSection Section to add or update
+ @param a_pKey Key to add or update.
+ @param a_nValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
+ notes on SetValue() for comments.
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/SetDoubleValue and
+ SetDoubleValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
+ way.
+
+ @return SI_Error See error definitions
+ @return SI_UPDATED Value was updated
+ @return SI_INSERTED Value was inserted
+ */
+ SI_Error SetDoubleValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ double a_nValue,
+ const SI_CHAR * a_pComment = NULL,
+ bool a_bForceReplace = false
+ );
+
+ /** Add or update a boolean value. This will always insert
+ when multiple keys are enabled.
+
+ @param a_pSection Section to add or update
+ @param a_pKey Key to add or update.
+ @param a_bValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
+ notes on SetValue() for comments.
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/SetBoolValue and
+ SetBoolValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
+ way.
+
+ @return SI_Error See error definitions
+ @return SI_UPDATED Value was updated
+ @return SI_INSERTED Value was inserted
+ */
+ SI_Error SetBoolValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bValue,
+ const SI_CHAR * a_pComment = NULL,
+ bool a_bForceReplace = false
+ );
+
+ /** Delete an entire section, or a key from a section. Note that the
+ data returned by GetSection is invalid and must not be used after
+ anything has been deleted from that section using this method.
+ Note when multiple keys is enabled, this will delete all keys with
+ that name; there is no way to selectively delete individual key/values
+ in this situation.
+
+ @param a_pSection Section to delete key from, or if
+ a_pKey is NULL, the section to remove.
+ @param a_pKey Key to remove from the section. Set to
+ NULL to remove the entire section.
+ @param a_bRemoveEmpty If the section is empty after this key has
+ been deleted, should the empty section be
+ removed?
+
+ @return true Key or section was deleted.
+ @return false Key or section was not found.
+ */
+ bool Delete(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bRemoveEmpty = false
+ );
+
+ /*-----------------------------------------------------------------------*/
+ /** @}
+ @{ @name Converter */
+
+ /** Return a conversion object to convert text to the same encoding
+ as is used by the Save(), SaveFile() and SaveString() functions.
+ Use this to prepare the strings that you wish to append or prepend
+ to the output INI data.
+ */
+ Converter GetConverter() const {
+ return Converter(m_bStoreIsUtf8);
+ }
+
+ /*-----------------------------------------------------------------------*/
+ /** @} */
+
+private:
+ // copying is not permitted
+ CSimpleIniTempl(const CSimpleIniTempl &); // disabled
+ CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled
+
+ /** Parse the data looking for a file comment and store it if found.
+ */
+ SI_Error FindFileComment(
+ SI_CHAR *& a_pData,
+ bool a_bCopyStrings
+ );
+
+ /** Parse the data looking for the next valid entry. The memory pointed to
+ by a_pData is modified by inserting NULL characters. The pointer is
+ updated to the current location in the block of text.
+ */
+ bool FindEntry(
+ SI_CHAR *& a_pData,
+ const SI_CHAR *& a_pSection,
+ const SI_CHAR *& a_pKey,
+ const SI_CHAR *& a_pVal,
+ const SI_CHAR *& a_pComment
+ ) const;
+
+ /** Add the section/key/value to our data.
+
+ @param a_pSection Section name. Sections will be created if they
+ don't already exist.
+ @param a_pKey Key name. May be NULL to create an empty section.
+ Existing entries will be updated. New entries will
+ be created.
+ @param a_pValue Value for the key.
+ @param a_pComment Comment to be associated with the section or the
+ key. If a_pKey is NULL then it will be associated
+ with the section, otherwise the key. This must be
+ a string in full comment form already (have a
+ comment character starting every line).
+ @param a_bForceReplace Should all existing values in a multi-key INI
+ file be replaced with this entry. This option has
+ no effect if not using multi-key files. The
+ difference between Delete/AddEntry and AddEntry
+ with a_bForceReplace = true, is that the load
+ order and comment will be preserved this way.
+ @param a_bCopyStrings Should copies of the strings be made or not.
+ If false then the pointers will be used as is.
+ */
+ SI_Error AddEntry(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pValue,
+ const SI_CHAR * a_pComment,
+ bool a_bForceReplace,
+ bool a_bCopyStrings
+ );
+
+ /** Is the supplied character a whitespace character? */
+ inline bool IsSpace(SI_CHAR ch) const {
+ return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
+ }
+
+ /** Does the supplied character start a comment line? */
+ inline bool IsComment(SI_CHAR ch) const {
+ return (ch == ';' || ch == '#');
+ }
+
+
+ /** Skip over a newline character (or characters) for either DOS or UNIX */
+ inline void SkipNewLine(SI_CHAR *& a_pData) const {
+ a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1;
+ }
+
+ /** Make a copy of the supplied string, replacing the original pointer */
+ SI_Error CopyString(const SI_CHAR *& a_pString);
+
+ /** Delete a string from the copied strings buffer if necessary */
+ void DeleteString(const SI_CHAR * a_pString);
+
+ /** Internal use of our string comparison function */
+ bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const {
+ const static SI_STRLESS isLess = SI_STRLESS();
+ return isLess(a_pLeft, a_pRight);
+ }
+
+ bool IsMultiLineTag(const SI_CHAR * a_pData) const;
+ bool IsMultiLineData(const SI_CHAR * a_pData) const;
+ bool LoadMultiLineText(
+ SI_CHAR *& a_pData,
+ const SI_CHAR *& a_pVal,
+ const SI_CHAR * a_pTagName,
+ bool a_bAllowBlankLinesInComment = false
+ ) const;
+ bool IsNewLineChar(SI_CHAR a_c) const;
+
+ bool OutputMultiLineText(
+ OutputWriter & a_oOutput,
+ Converter & a_oConverter,
+ const SI_CHAR * a_pText
+ ) const;
+
+private:
+ /** Copy of the INI file data in our character format. This will be
+ modified when parsed to have NULL characters added after all
+ interesting string entries. All of the string pointers to sections,
+ keys and values point into this block of memory.
+ */
+ SI_CHAR * m_pData;
+
+ /** Length of the data that we have stored. Used when deleting strings
+ to determine if the string is stored here or in the allocated string
+ buffer.
+ */
+ size_t m_uDataLen;
+
+ /** File comment for this data, if one exists. */
+ const SI_CHAR * m_pFileComment;
+
+ /** Parsed INI data. Section -> (Key -> Value). */
+ TSection m_data;
+
+ /** This vector stores allocated memory for copies of strings that have
+ been supplied after the file load. It will be empty unless SetValue()
+ has been called.
+ */
+ TNamesDepend m_strings;
+
+ /** Is the format of our datafile UTF-8 or MBCS? */
+ bool m_bStoreIsUtf8;
+
+ /** Are multiple values permitted for the same key? */
+ bool m_bAllowMultiKey;
+
+ /** Are data values permitted to span multiple lines? */
+ bool m_bAllowMultiLine;
+
+ /** Should spaces be written out surrounding the equals sign? */
+ bool m_bSpaces;
+
+ /** Next order value, used to ensure sections and keys are output in the
+ same order that they are loaded/added.
+ */
+ int m_nOrder;
+};
+
+// ---------------------------------------------------------------------------
+// IMPLEMENTATION
+// ---------------------------------------------------------------------------
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl(
+ bool a_bIsUtf8,
+ bool a_bAllowMultiKey,
+ bool a_bAllowMultiLine
+ )
+ : m_pData(0)
+ , m_uDataLen(0)
+ , m_pFileComment(NULL)
+ , m_bStoreIsUtf8(a_bIsUtf8)
+ , m_bAllowMultiKey(a_bAllowMultiKey)
+ , m_bAllowMultiLine(a_bAllowMultiLine)
+ , m_bSpaces(true)
+ , m_nOrder(0)
+{ }
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::~CSimpleIniTempl()
+{
+ Reset();
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+void
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Reset()
+{
+ // remove all data
+ delete[] m_pData;
+ m_pData = NULL;
+ m_uDataLen = 0;
+ m_pFileComment = NULL;
+ if (!m_data.empty()) {
+ m_data.erase(m_data.begin(), m_data.end());
+ }
+
+ // remove all strings
+ if (!m_strings.empty()) {
+ typename TNamesDepend::iterator i = m_strings.begin();
+ for (; i != m_strings.end(); ++i) {
+ delete[] const_cast<SI_CHAR*>(i->pItem);
+ }
+ m_strings.erase(m_strings.begin(), m_strings.end());
+ }
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
+ const char * a_pszFile
+ )
+{
+ FILE * fp = NULL;
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ fopen_s(&fp, a_pszFile, "rb");
+#else // !__STDC_WANT_SECURE_LIB__
+ fp = fopen(a_pszFile, "rb");
+#endif // __STDC_WANT_SECURE_LIB__
+ if (!fp) {
+ return SI_FILE;
+ }
+ SI_Error rc = LoadFile(fp);
+ fclose(fp);
+ return rc;
+}
+
+#ifdef SI_HAS_WIDE_FILE
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
+ const SI_WCHAR_T * a_pwszFile
+ )
+{
+#ifdef _WIN32
+ FILE * fp = NULL;
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ _wfopen_s(&fp, a_pwszFile, L"rb");
+#else // !__STDC_WANT_SECURE_LIB__
+ fp = _wfopen(a_pwszFile, L"rb");
+#endif // __STDC_WANT_SECURE_LIB__
+ if (!fp) return SI_FILE;
+ SI_Error rc = LoadFile(fp);
+ fclose(fp);
+ return rc;
+#else // !_WIN32 (therefore SI_CONVERT_ICU)
+ char szFile[256];
+ u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
+ return LoadFile(szFile);
+#endif // _WIN32
+}
+#endif // SI_HAS_WIDE_FILE
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
+ FILE * a_fpFile
+ )
+{
+ // load the raw file data
+ int retval = fseek(a_fpFile, 0, SEEK_END);
+ if (retval != 0) {
+ return SI_FILE;
+ }
+ long lSize = ftell(a_fpFile);
+ if (lSize < 0) {
+ return SI_FILE;
+ }
+ if (lSize == 0) {
+ return SI_OK;
+ }
+ char * pData = new char[lSize];
+ if (!pData) {
+ return SI_NOMEM;
+ }
+ fseek(a_fpFile, 0, SEEK_SET);
+ size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
+ if (uRead != (size_t) lSize) {
+ delete[] pData;
+ return SI_FILE;
+ }
+
+ // convert the raw data to unicode
+ SI_Error rc = LoadData(pData, uRead);
+ delete[] pData;
+ return rc;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(
+ const char * a_pData,
+ size_t a_uDataLen
+ )
+{
+ SI_CONVERTER converter(m_bStoreIsUtf8);
+
+ if (a_uDataLen == 0) {
+ return SI_OK;
+ }
+
+ // consume the UTF-8 BOM if it exists
+ if (m_bStoreIsUtf8 && a_uDataLen >= 3) {
+ if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
+ a_pData += 3;
+ a_uDataLen -= 3;
+ }
+ }
+
+ // determine the length of the converted data
+ size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
+ if (uLen == (size_t)(-1)) {
+ return SI_FAIL;
+ }
+
+ // allocate memory for the data, ensure that there is a NULL
+ // terminator wherever the converted data ends
+ SI_CHAR * pData = new SI_CHAR[uLen+1];
+ if (!pData) {
+ return SI_NOMEM;
+ }
+ memset(pData, 0, sizeof(SI_CHAR)*(uLen+1));
+
+ // convert the data
+ if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
+ delete[] pData;
+ return SI_FAIL;
+ }
+
+ // parse it
+ const static SI_CHAR empty = 0;
+ SI_CHAR * pWork = pData;
+ const SI_CHAR * pSection = &empty;
+ const SI_CHAR * pItem = NULL;
+ const SI_CHAR * pVal = NULL;
+ const SI_CHAR * pComment = NULL;
+
+ // We copy the strings if we are loading data into this class when we
+ // already have stored some.
+ bool bCopyStrings = (m_pData != NULL);
+
+ // find a file comment if it exists, this is a comment that starts at the
+ // beginning of the file and continues until the first blank line.
+ SI_Error rc = FindFileComment(pWork, bCopyStrings);
+ if (rc < 0) return rc;
+
+ // add every entry in the file to the data table
+ while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
+ rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings);
+ if (rc < 0) return rc;
+ }
+
+ // store these strings if we didn't copy them
+ if (bCopyStrings) {
+ delete[] pData;
+ }
+ else {
+ m_pData = pData;
+ m_uDataLen = uLen+1;
+ }
+
+ return SI_OK;
+}
+
+#ifdef SI_SUPPORT_IOSTREAMS
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(
+ std::istream & a_istream
+ )
+{
+ std::string strData;
+ char szBuf[512];
+ do {
+ a_istream.get(szBuf, sizeof(szBuf), '\0');
+ strData.append(szBuf);
+ }
+ while (a_istream.good());
+ return LoadData(strData);
+}
+#endif // SI_SUPPORT_IOSTREAMS
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindFileComment(
+ SI_CHAR *& a_pData,
+ bool a_bCopyStrings
+ )
+{
+ // there can only be a single file comment
+ if (m_pFileComment) {
+ return SI_OK;
+ }
+
+ // Load the file comment as multi-line text, this will modify all of
+ // the newline characters to be single \n chars
+ if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
+ return SI_OK;
+ }
+
+ // copy the string if necessary
+ if (a_bCopyStrings) {
+ SI_Error rc = CopyString(m_pFileComment);
+ if (rc < 0) return rc;
+ }
+
+ return SI_OK;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
+ SI_CHAR *& a_pData,
+ const SI_CHAR *& a_pSection,
+ const SI_CHAR *& a_pKey,
+ const SI_CHAR *& a_pVal,
+ const SI_CHAR *& a_pComment
+ ) const
+{
+ a_pComment = NULL;
+
+ SI_CHAR * pTrail = NULL;
+ while (*a_pData) {
+ // skip spaces and empty lines
+ while (*a_pData && IsSpace(*a_pData)) {
+ ++a_pData;
+ }
+ if (!*a_pData) {
+ break;
+ }
+
+ // skip processing of comment lines but keep a pointer to
+ // the start of the comment.
+ if (IsComment(*a_pData)) {
+ LoadMultiLineText(a_pData, a_pComment, NULL, true);
+ continue;
+ }
+
+ // process section names
+ if (*a_pData == '[') {
+ // skip leading spaces
+ ++a_pData;
+ while (*a_pData && IsSpace(*a_pData)) {
+ ++a_pData;
+ }
+
+ // find the end of the section name (it may contain spaces)
+ // and convert it to lowercase as necessary
+ a_pSection = a_pData;
+ while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ // if it's an invalid line, just skip it
+ if (*a_pData != ']') {
+ continue;
+ }
+
+ // remove trailing spaces from the section
+ pTrail = a_pData - 1;
+ while (pTrail >= a_pSection && IsSpace(*pTrail)) {
+ --pTrail;
+ }
+ ++pTrail;
+ *pTrail = 0;
+
+ // skip to the end of the line
+ ++a_pData; // safe as checked that it == ']' above
+ while (*a_pData && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ a_pKey = NULL;
+ a_pVal = NULL;
+ return true;
+ }
+
+ // find the end of the key name (it may contain spaces)
+ // and convert it to lowercase as necessary
+ a_pKey = a_pData;
+ while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ // if it's an invalid line, just skip it
+ if (*a_pData != '=') {
+ continue;
+ }
+
+ // empty keys are invalid
+ if (a_pKey == a_pData) {
+ while (*a_pData && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+ continue;
+ }
+
+ // remove trailing spaces from the key
+ pTrail = a_pData - 1;
+ while (pTrail >= a_pKey && IsSpace(*pTrail)) {
+ --pTrail;
+ }
+ ++pTrail;
+ *pTrail = 0;
+
+ // skip leading whitespace on the value
+ ++a_pData; // safe as checked that it == '=' above
+ while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
+ ++a_pData;
+ }
+
+ // find the end of the value which is the end of this line
+ a_pVal = a_pData;
+ while (*a_pData && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ // remove trailing spaces from the value
+ pTrail = a_pData - 1;
+ if (*a_pData) { // prepare for the next round
+ SkipNewLine(a_pData);
+ }
+ while (pTrail >= a_pVal && IsSpace(*pTrail)) {
+ --pTrail;
+ }
+ ++pTrail;
+ *pTrail = 0;
+
+ // check for multi-line entries
+ if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
+ // skip the "<<<" to get the tag that will end the multiline
+ const SI_CHAR * pTagName = a_pVal + 3;
+ return LoadMultiLineText(a_pData, a_pVal, pTagName);
+ }
+
+ // return the standard entry
+ return true;
+ }
+
+ return false;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineTag(
+ const SI_CHAR * a_pVal
+ ) const
+{
+ // check for the "<<<" prefix for a multi-line entry
+ if (*a_pVal++ != '<') return false;
+ if (*a_pVal++ != '<') return false;
+ if (*a_pVal++ != '<') return false;
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData(
+ const SI_CHAR * a_pData
+ ) const
+{
+ // data is multi-line if it has any of the following features:
+ // * whitespace prefix
+ // * embedded newlines
+ // * whitespace suffix
+
+ // empty string
+ if (!*a_pData) {
+ return false;
+ }
+
+ // check for prefix
+ if (IsSpace(*a_pData)) {
+ return true;
+ }
+
+ // embedded newlines
+ while (*a_pData) {
+ if (IsNewLineChar(*a_pData)) {
+ return true;
+ }
+ ++a_pData;
+ }
+
+ // check for suffix
+ if (IsSpace(*--a_pData)) {
+ return true;
+ }
+
+ return false;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar(
+ SI_CHAR a_c
+ ) const
+{
+ return (a_c == '\n' || a_c == '\r');
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
+ SI_CHAR *& a_pData,
+ const SI_CHAR *& a_pVal,
+ const SI_CHAR * a_pTagName,
+ bool a_bAllowBlankLinesInComment
+ ) const
+{
+ // we modify this data to strip all newlines down to a single '\n'
+ // character. This means that on Windows we need to strip out some
+ // characters which will make the data shorter.
+ // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
+ // LINE1-LINE1\nLINE2-LINE2\0
+ // The pDataLine entry is the pointer to the location in memory that
+ // the current line needs to start to run following the existing one.
+ // This may be the same as pCurrLine in which case no move is needed.
+ SI_CHAR * pDataLine = a_pData;
+ SI_CHAR * pCurrLine;
+
+ // value starts at the current line
+ a_pVal = a_pData;
+
+ // find the end tag. This tag must start in column 1 and be
+ // followed by a newline. No whitespace removal is done while
+ // searching for this tag.
+ SI_CHAR cEndOfLineChar = *a_pData;
+ for(;;) {
+ // if we are loading comments then we need a comment character as
+ // the first character on every line
+ if (!a_pTagName && !IsComment(*a_pData)) {
+ // if we aren't allowing blank lines then we're done
+ if (!a_bAllowBlankLinesInComment) {
+ break;
+ }
+
+ // if we are allowing blank lines then we only include them
+ // in this comment if another comment follows, so read ahead
+ // to find out.
+ SI_CHAR * pCurr = a_pData;
+ int nNewLines = 0;
+ while (IsSpace(*pCurr)) {
+ if (IsNewLineChar(*pCurr)) {
+ ++nNewLines;
+ SkipNewLine(pCurr);
+ }
+ else {
+ ++pCurr;
+ }
+ }
+
+ // we have a comment, add the blank lines to the output
+ // and continue processing from here
+ if (IsComment(*pCurr)) {
+ for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n';
+ a_pData = pCurr;
+ continue;
+ }
+
+ // the comment ends here
+ break;
+ }
+
+ // find the end of this line
+ pCurrLine = a_pData;
+ while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
+
+ // move this line down to the location that it should be if necessary
+ if (pDataLine < pCurrLine) {
+ size_t nLen = (size_t) (a_pData - pCurrLine);
+ memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
+ pDataLine[nLen] = '\0';
+ }
+
+ // end the line with a NULL
+ cEndOfLineChar = *a_pData;
+ *a_pData = 0;
+
+ // if are looking for a tag then do the check now. This is done before
+ // checking for end of the data, so that if we have the tag at the end
+ // of the data then the tag is removed correctly.
+ if (a_pTagName &&
+ (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)))
+ {
+ break;
+ }
+
+ // if we are at the end of the data then we just automatically end
+ // this entry and return the current data.
+ if (!cEndOfLineChar) {
+ return true;
+ }
+
+ // otherwise we need to process this newline to ensure that it consists
+ // of just a single \n character.
+ pDataLine += (a_pData - pCurrLine);
+ *a_pData = cEndOfLineChar;
+ SkipNewLine(a_pData);
+ *pDataLine++ = '\n';
+ }
+
+ // if we didn't find a comment at all then return false
+ if (a_pVal == a_pData) {
+ a_pVal = NULL;
+ return false;
+ }
+
+ // the data (which ends at the end of the last line) needs to be
+ // null-terminated BEFORE before the newline character(s). If the
+ // user wants a new line in the multi-line data then they need to
+ // add an empty line before the tag.
+ *--pDataLine = '\0';
+
+ // if looking for a tag and if we aren't at the end of the data,
+ // then move a_pData to the start of the next line.
+ if (a_pTagName && cEndOfLineChar) {
+ SI_ASSERT(IsNewLineChar(cEndOfLineChar));
+ *a_pData = cEndOfLineChar;
+ SkipNewLine(a_pData);
+ }
+
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString(
+ const SI_CHAR *& a_pString
+ )
+{
+ size_t uLen = 0;
+ if (sizeof(SI_CHAR) == sizeof(char)) {
+ uLen = strlen((const char *)a_pString);
+ }
+ else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
+ uLen = wcslen((const wchar_t *)a_pString);
+ }
+ else {
+ for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
+ }
+ ++uLen; // NULL character
+ SI_CHAR * pCopy = new SI_CHAR[uLen];
+ if (!pCopy) {
+ return SI_NOMEM;
+ }
+ memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
+ m_strings.push_back(pCopy);
+ a_pString = pCopy;
+ return SI_OK;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pValue,
+ const SI_CHAR * a_pComment,
+ bool a_bForceReplace,
+ bool a_bCopyStrings
+ )
+{
+ SI_Error rc;
+ bool bInserted = false;
+
+ SI_ASSERT(!a_pComment || IsComment(*a_pComment));
+
+ // if we are copying strings then make a copy of the comment now
+ // because we will need it when we add the entry.
+ if (a_bCopyStrings && a_pComment) {
+ rc = CopyString(a_pComment);
+ if (rc < 0) return rc;
+ }
+
+ // create the section entry if necessary
+ typename TSection::iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ // if the section doesn't exist then we need a copy as the
+ // string needs to last beyond the end of this function
+ if (a_bCopyStrings) {
+ rc = CopyString(a_pSection);
+ if (rc < 0) return rc;
+ }
+
+ // only set the comment if this is a section only entry
+ Entry oSection(a_pSection, ++m_nOrder);
+ if (a_pComment && (!a_pKey || !a_pValue)) {
+ oSection.pComment = a_pComment;
+ }
+
+ typename TSection::value_type oEntry(oSection, TKeyVal());
+ typedef typename TSection::iterator SectionIterator;
+ std::pair<SectionIterator,bool> i = m_data.insert(oEntry);
+ iSection = i.first;
+ bInserted = true;
+ }
+ if (!a_pKey || !a_pValue) {
+ // section only entries are specified with pItem and pVal as NULL
+ return bInserted ? SI_INSERTED : SI_UPDATED;
+ }
+
+ // check for existence of the key
+ TKeyVal & keyval = iSection->second;
+ typename TKeyVal::iterator iKey = keyval.find(a_pKey);
+
+ // remove all existing entries but save the load order and
+ // comment of the first entry
+ int nLoadOrder = ++m_nOrder;
+ if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) {
+ const SI_CHAR * pComment = NULL;
+ while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) {
+ if (iKey->first.nOrder < nLoadOrder) {
+ nLoadOrder = iKey->first.nOrder;
+ pComment = iKey->first.pComment;
+ }
+ ++iKey;
+ }
+ if (pComment) {
+ DeleteString(a_pComment);
+ a_pComment = pComment;
+ CopyString(a_pComment);
+ }
+ Delete(a_pSection, a_pKey);
+ iKey = keyval.end();
+ }
+
+ // make string copies if necessary
+ bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
+ if (a_bCopyStrings) {
+ if (bForceCreateNewKey || iKey == keyval.end()) {
+ // if the key doesn't exist then we need a copy as the
+ // string needs to last beyond the end of this function
+ // because we will be inserting the key next
+ rc = CopyString(a_pKey);
+ if (rc < 0) return rc;
+ }
+
+ // we always need a copy of the value
+ rc = CopyString(a_pValue);
+ if (rc < 0) return rc;
+ }
+
+ // create the key entry
+ if (iKey == keyval.end() || bForceCreateNewKey) {
+ Entry oKey(a_pKey, nLoadOrder);
+ if (a_pComment) {
+ oKey.pComment = a_pComment;
+ }
+ typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL));
+ iKey = keyval.insert(oEntry);
+ bInserted = true;
+ }
+ iKey->second = a_pValue;
+ return bInserted ? SI_INSERTED : SI_UPDATED;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+const SI_CHAR *
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pDefault,
+ bool * a_pHasMultiple
+ ) const
+{
+ if (a_pHasMultiple) {
+ *a_pHasMultiple = false;
+ }
+ if (!a_pSection || !a_pKey) {
+ return a_pDefault;
+ }
+ typename TSection::const_iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return a_pDefault;
+ }
+ typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
+ if (iKeyVal == iSection->second.end()) {
+ return a_pDefault;
+ }
+
+ // check for multiple entries with the same key
+ if (m_bAllowMultiKey && a_pHasMultiple) {
+ typename TKeyVal::const_iterator iTemp = iKeyVal;
+ if (++iTemp != iSection->second.end()) {
+ if (!IsLess(a_pKey, iTemp->first.pItem)) {
+ *a_pHasMultiple = true;
+ }
+ }
+ }
+
+ return iKeyVal->second;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+long
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetLongValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ long a_nDefault,
+ bool * a_pHasMultiple
+ ) const
+{
+ // return the default if we don't have a value
+ const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
+ if (!pszValue || !*pszValue) return a_nDefault;
+
+ // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
+ char szValue[64] = { 0 };
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
+ return a_nDefault;
+ }
+
+ // handle the value as hex if prefaced with "0x"
+ long nValue = a_nDefault;
+ char * pszSuffix = szValue;
+ if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
+ if (!szValue[2]) return a_nDefault;
+ nValue = strtol(&szValue[2], &pszSuffix, 16);
+ }
+ else {
+ nValue = strtol(szValue, &pszSuffix, 10);
+ }
+
+ // any invalid strings will return the default value
+ if (*pszSuffix) {
+ return a_nDefault;
+ }
+
+ return nValue;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ long a_nValue,
+ const SI_CHAR * a_pComment,
+ bool a_bUseHex,
+ bool a_bForceReplace
+ )
+{
+ // use SetValue to create sections
+ if (!a_pSection || !a_pKey) return SI_FAIL;
+
+ // convert to an ASCII string
+ char szInput[64];
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
+#else // !__STDC_WANT_SECURE_LIB__
+ sprintf(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
+#endif // __STDC_WANT_SECURE_LIB__
+
+ // convert to output text
+ SI_CHAR szOutput[64];
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ c.ConvertFromStore(szInput, strlen(szInput) + 1,
+ szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
+
+ // actually add it
+ return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+double
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetDoubleValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ double a_nDefault,
+ bool * a_pHasMultiple
+ ) const
+{
+ // return the default if we don't have a value
+ const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
+ if (!pszValue || !*pszValue) return a_nDefault;
+
+ // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
+ char szValue[64] = { 0 };
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
+ return a_nDefault;
+ }
+
+ char * pszSuffix = NULL;
+ double nValue = strtod(szValue, &pszSuffix);
+
+ // any invalid strings will return the default value
+ if (!pszSuffix || *pszSuffix) {
+ return a_nDefault;
+ }
+
+ return nValue;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetDoubleValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ double a_nValue,
+ const SI_CHAR * a_pComment,
+ bool a_bForceReplace
+ )
+{
+ // use SetValue to create sections
+ if (!a_pSection || !a_pKey) return SI_FAIL;
+
+ // convert to an ASCII string
+ char szInput[64];
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ sprintf_s(szInput, "%f", a_nValue);
+#else // !__STDC_WANT_SECURE_LIB__
+ sprintf(szInput, "%f", a_nValue);
+#endif // __STDC_WANT_SECURE_LIB__
+
+ // convert to output text
+ SI_CHAR szOutput[64];
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ c.ConvertFromStore(szInput, strlen(szInput) + 1,
+ szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
+
+ // actually add it
+ return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetBoolValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bDefault,
+ bool * a_pHasMultiple
+ ) const
+{
+ // return the default if we don't have a value
+ const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
+ if (!pszValue || !*pszValue) return a_bDefault;
+
+ // we only look at the minimum number of characters
+ switch (pszValue[0]) {
+ case 't': case 'T': // true
+ case 'y': case 'Y': // yes
+ case '1': // 1 (one)
+ return true;
+
+ case 'f': case 'F': // false
+ case 'n': case 'N': // no
+ case '0': // 0 (zero)
+ return false;
+
+ case 'o': case 'O':
+ if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on
+ if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off
+ break;
+ }
+
+ // no recognized value, return the default
+ return a_bDefault;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetBoolValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bValue,
+ const SI_CHAR * a_pComment,
+ bool a_bForceReplace
+ )
+{
+ // use SetValue to create sections
+ if (!a_pSection || !a_pKey) return SI_FAIL;
+
+ // convert to an ASCII string
+ const char * pszInput = a_bValue ? "true" : "false";
+
+ // convert to output text
+ SI_CHAR szOutput[64];
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ c.ConvertFromStore(pszInput, strlen(pszInput) + 1,
+ szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
+
+ // actually add it
+ return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ TNamesDepend & a_values
+ ) const
+{
+ a_values.clear();
+
+ if (!a_pSection || !a_pKey) {
+ return false;
+ }
+ typename TSection::const_iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return false;
+ }
+ typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
+ if (iKeyVal == iSection->second.end()) {
+ return false;
+ }
+
+ // insert all values for this key
+ a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
+ if (m_bAllowMultiKey) {
+ ++iKeyVal;
+ while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {
+ a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
+ ++iKeyVal;
+ }
+ }
+
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+int
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSectionSize(
+ const SI_CHAR * a_pSection
+ ) const
+{
+ if (!a_pSection) {
+ return -1;
+ }
+
+ typename TSection::const_iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return -1;
+ }
+ const TKeyVal & section = iSection->second;
+
+ // if multi-key isn't permitted then the section size is
+ // the number of keys that we have.
+ if (!m_bAllowMultiKey || section.empty()) {
+ return (int) section.size();
+ }
+
+ // otherwise we need to count them
+ int nCount = 0;
+ const SI_CHAR * pLastKey = NULL;
+ typename TKeyVal::const_iterator iKeyVal = section.begin();
+ for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
+ if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
+ ++nCount;
+ pLastKey = iKeyVal->first.pItem;
+ }
+ }
+ return nCount;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+const typename CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::TKeyVal *
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSection(
+ const SI_CHAR * a_pSection
+ ) const
+{
+ if (a_pSection) {
+ typename TSection::const_iterator i = m_data.find(a_pSection);
+ if (i != m_data.end()) {
+ return &(i->second);
+ }
+ }
+ return 0;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+void
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections(
+ TNamesDepend & a_names
+ ) const
+{
+ a_names.clear();
+ typename TSection::const_iterator i = m_data.begin();
+ for (int n = 0; i != m_data.end(); ++i, ++n ) {
+ a_names.push_back(i->first);
+ }
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllKeys(
+ const SI_CHAR * a_pSection,
+ TNamesDepend & a_names
+ ) const
+{
+ a_names.clear();
+
+ if (!a_pSection) {
+ return false;
+ }
+
+ typename TSection::const_iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return false;
+ }
+
+ const TKeyVal & section = iSection->second;
+ const SI_CHAR * pLastKey = NULL;
+ typename TKeyVal::const_iterator iKeyVal = section.begin();
+ for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) {
+ if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
+ a_names.push_back(iKeyVal->first);
+ pLastKey = iKeyVal->first.pItem;
+ }
+ }
+
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
+ const char * a_pszFile,
+ bool a_bAddSignature
+ ) const
+{
+ FILE * fp = NULL;
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ fopen_s(&fp, a_pszFile, "wb");
+#else // !__STDC_WANT_SECURE_LIB__
+ fp = fopen(a_pszFile, "wb");
+#endif // __STDC_WANT_SECURE_LIB__
+ if (!fp) return SI_FILE;
+ SI_Error rc = SaveFile(fp, a_bAddSignature);
+ fclose(fp);
+ return rc;
+}
+
+#ifdef SI_HAS_WIDE_FILE
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
+ const SI_WCHAR_T * a_pwszFile,
+ bool a_bAddSignature
+ ) const
+{
+#ifdef _WIN32
+ FILE * fp = NULL;
+#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
+ _wfopen_s(&fp, a_pwszFile, L"wb");
+#else // !__STDC_WANT_SECURE_LIB__
+ fp = _wfopen(a_pwszFile, L"wb");
+#endif // __STDC_WANT_SECURE_LIB__
+ if (!fp) return SI_FILE;
+ SI_Error rc = SaveFile(fp, a_bAddSignature);
+ fclose(fp);
+ return rc;
+#else // !_WIN32 (therefore SI_CONVERT_ICU)
+ char szFile[256];
+ u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
+ return SaveFile(szFile, a_bAddSignature);
+#endif // _WIN32
+}
+#endif // SI_HAS_WIDE_FILE
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
+ FILE * a_pFile,
+ bool a_bAddSignature
+ ) const
+{
+ FileWriter writer(a_pFile);
+ return Save(writer, a_bAddSignature);
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+SI_Error
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
+ OutputWriter & a_oOutput,
+ bool a_bAddSignature
+ ) const
+{
+ Converter convert(m_bStoreIsUtf8);
+
+ // add the UTF-8 signature if it is desired
+ if (m_bStoreIsUtf8 && a_bAddSignature) {
+ a_oOutput.Write(SI_UTF8_SIGNATURE);
+ }
+
+ // get all of the sections sorted in load order
+ TNamesDepend oSections;
+ GetAllSections(oSections);
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+ oSections.sort();
+#elif defined(__BORLANDC__)
+ oSections.sort(Entry::LoadOrder());
+#else
+ oSections.sort(typename Entry::LoadOrder());
+#endif
+
+ // write the file comment if we have one
+ bool bNeedNewLine = false;
+ if (m_pFileComment) {
+ if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
+ return SI_FAIL;
+ }
+ bNeedNewLine = true;
+ }
+
+ // iterate through our sections and output the data
+ typename TNamesDepend::const_iterator iSection = oSections.begin();
+ for ( ; iSection != oSections.end(); ++iSection ) {
+ // write out the comment if there is one
+ if (iSection->pComment) {
+ if (bNeedNewLine) {
+ a_oOutput.Write(SI_NEWLINE_A);
+ a_oOutput.Write(SI_NEWLINE_A);
+ }
+ if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) {
+ return SI_FAIL;
+ }
+ bNeedNewLine = false;
+ }
+
+ if (bNeedNewLine) {
+ a_oOutput.Write(SI_NEWLINE_A);
+ a_oOutput.Write(SI_NEWLINE_A);
+ bNeedNewLine = false;
+ }
+
+ // write the section (unless there is no section name)
+ if (*iSection->pItem) {
+ if (!convert.ConvertToStore(iSection->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write("[");
+ a_oOutput.Write(convert.Data());
+ a_oOutput.Write("]");
+ a_oOutput.Write(SI_NEWLINE_A);
+ }
+
+ // get all of the keys sorted in load order
+ TNamesDepend oKeys;
+ GetAllKeys(iSection->pItem, oKeys);
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+ oKeys.sort();
+#elif defined(__BORLANDC__)
+ oKeys.sort(Entry::LoadOrder());
+#else
+ oKeys.sort(typename Entry::LoadOrder());
+#endif
+
+ // write all keys and values
+ typename TNamesDepend::const_iterator iKey = oKeys.begin();
+ for ( ; iKey != oKeys.end(); ++iKey) {
+ // get all values for this key
+ TNamesDepend oValues;
+ GetAllValues(iSection->pItem, iKey->pItem, oValues);
+
+ typename TNamesDepend::const_iterator iValue = oValues.begin();
+ for ( ; iValue != oValues.end(); ++iValue) {
+ // write out the comment if there is one
+ if (iValue->pComment) {
+ a_oOutput.Write(SI_NEWLINE_A);
+ if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) {
+ return SI_FAIL;
+ }
+ }
+
+ // write the key
+ if (!convert.ConvertToStore(iKey->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write(convert.Data());
+
+ // write the value
+ if (!convert.ConvertToStore(iValue->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write(m_bSpaces ? " = " : "=");
+ if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
+ // multi-line data needs to be processed specially to ensure
+ // that we use the correct newline format for the current system
+ a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
+ if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write("END_OF_TEXT");
+ }
+ else {
+ a_oOutput.Write(convert.Data());
+ }
+ a_oOutput.Write(SI_NEWLINE_A);
+ }
+ }
+
+ bNeedNewLine = true;
+ }
+
+ return SI_OK;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::OutputMultiLineText(
+ OutputWriter & a_oOutput,
+ Converter & a_oConverter,
+ const SI_CHAR * a_pText
+ ) const
+{
+ const SI_CHAR * pEndOfLine;
+ SI_CHAR cEndOfLineChar = *a_pText;
+ while (cEndOfLineChar) {
+ // find the end of this line
+ pEndOfLine = a_pText;
+ for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;
+ cEndOfLineChar = *pEndOfLine;
+
+ // temporarily null terminate, convert and output the line
+ *const_cast<SI_CHAR*>(pEndOfLine) = 0;
+ if (!a_oConverter.ConvertToStore(a_pText)) {
+ return false;
+ }
+ *const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar;
+ a_pText += (pEndOfLine - a_pText) + 1;
+ a_oOutput.Write(a_oConverter.Data());
+ a_oOutput.Write(SI_NEWLINE_A);
+ }
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ bool a_bRemoveEmpty
+ )
+{
+ if (!a_pSection) {
+ return false;
+ }
+
+ typename TSection::iterator iSection = m_data.find(a_pSection);
+ if (iSection == m_data.end()) {
+ return false;
+ }
+
+ // remove a single key if we have a keyname
+ if (a_pKey) {
+ typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
+ if (iKeyVal == iSection->second.end()) {
+ return false;
+ }
+
+ // remove any copied strings and then the key
+ typename TKeyVal::iterator iDelete;
+ do {
+ iDelete = iKeyVal++;
+
+ DeleteString(iDelete->first.pItem);
+ DeleteString(iDelete->second);
+ iSection->second.erase(iDelete);
+ }
+ while (iKeyVal != iSection->second.end()
+ && !IsLess(a_pKey, iKeyVal->first.pItem));
+
+ // done now if the section is not empty or we are not pruning away
+ // the empty sections. Otherwise let it fall through into the section
+ // deletion code
+ if (!a_bRemoveEmpty || !iSection->second.empty()) {
+ return true;
+ }
+ }
+ else {
+ // delete all copied strings from this section. The actual
+ // entries will be removed when the section is removed.
+ typename TKeyVal::iterator iKeyVal = iSection->second.begin();
+ for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
+ DeleteString(iKeyVal->first.pItem);
+ DeleteString(iKeyVal->second);
+ }
+ }
+
+ // delete the section itself
+ DeleteString(iSection->first.pItem);
+ m_data.erase(iSection);
+
+ return true;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+void
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString(
+ const SI_CHAR * a_pString
+ )
+{
+ // strings may exist either inside the data block, or they will be
+ // individually allocated and stored in m_strings. We only physically
+ // delete those stored in m_strings.
+ if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
+ typename TNamesDepend::iterator i = m_strings.begin();
+ for (;i != m_strings.end(); ++i) {
+ if (a_pString == i->pItem) {
+ delete[] const_cast<SI_CHAR*>(i->pItem);
+ m_strings.erase(i);
+ break;
+ }
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+// CONVERSION FUNCTIONS
+// ---------------------------------------------------------------------------
+
+// Defines the conversion classes for different libraries. Before including
+// SimpleIni.h, set the converter that you wish you use by defining one of the
+// following symbols.
+//
+// SI_CONVERT_GENERIC Use the Unicode reference conversion library in
+// the accompanying files ConvertUTF.h/c
+// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
+// ICU headers on include path and icuuc.lib
+// SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
+
+#if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
+# ifdef _WIN32
+# define SI_CONVERT_WIN32
+# else
+# define SI_CONVERT_GENERIC
+# endif
+#endif
+
+/**
+ * Generic case-sensitive less than comparison. This class returns numerically
+ * ordered ASCII case-sensitive text for all possible sizes and types of
+ * SI_CHAR.
+ */
+template<class SI_CHAR>
+struct SI_GenericCase {
+ bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
+ long cmp;
+ for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
+ cmp = (long) *pLeft - (long) *pRight;
+ if (cmp != 0) {
+ return cmp < 0;
+ }
+ }
+ return *pRight != 0;
+ }
+};
+
+/**
+ * Generic ASCII case-insensitive less than comparison. This class returns
+ * numerically ordered ASCII case-insensitive text for all possible sizes
+ * and types of SI_CHAR. It is not safe for MBCS text comparison where
+ * ASCII A-Z characters are used in the encoding of multi-byte characters.
+ */
+template<class SI_CHAR>
+struct SI_GenericNoCase {
+ inline SI_CHAR locase(SI_CHAR ch) const {
+ return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
+ }
+ bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
+ long cmp;
+ for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
+ cmp = (long) locase(*pLeft) - (long) locase(*pRight);
+ if (cmp != 0) {
+ return cmp < 0;
+ }
+ }
+ return *pRight != 0;
+ }
+};
+
+/**
+ * Null conversion class for MBCS/UTF-8 to char (or equivalent).
+ */
+template<class SI_CHAR>
+class SI_ConvertA {
+ bool m_bStoreIsUtf8;
+protected:
+ SI_ConvertA() { }
+public:
+ SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
+
+ /* copy and assignment */
+ SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }
+ SI_ConvertA & operator=(const SI_ConvertA & rhs) {
+ m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
+ return *this;
+ }
+
+ /** Calculate the number of SI_CHAR required for converting the input
+ * from the storage format. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @return Number of SI_CHAR required by the string when
+ * converted. If there are embedded NULL bytes in the
+ * input data, only the string up and not including
+ * the NULL byte will be converted.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen)
+ {
+ (void)a_pInputData;
+ SI_ASSERT(a_uInputDataLen != (size_t) -1);
+
+ // ASCII/MBCS/UTF-8 needs no conversion
+ return a_uInputDataLen;
+ }
+
+ /** Convert the input string from the storage format to SI_CHAR.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @param a_pOutputData Pointer to the output buffer to received the
+ * converted data.
+ * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
+ * @return true if all of the input data was successfully
+ * converted.
+ */
+ bool ConvertFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen,
+ SI_CHAR * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ // ASCII/MBCS/UTF-8 needs no conversion
+ if (a_uInputDataLen > a_uOutputDataSize) {
+ return false;
+ }
+ memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
+ return true;
+ }
+
+ /** Calculate the number of char required by the storage format of this
+ * data. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated string to calculate the number of
+ * bytes required to be converted to storage format.
+ * @return Number of bytes required by the string when
+ * converted to storage format. This size always
+ * includes space for the terminating NULL character.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeToStore(
+ const SI_CHAR * a_pInputData)
+ {
+ // ASCII/MBCS/UTF-8 needs no conversion
+ return strlen((const char *)a_pInputData) + 1;
+ }
+
+ /** Convert the input string to the storage format of this data.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated source string to convert. All of
+ * the data will be converted including the
+ * terminating NULL character.
+ * @param a_pOutputData Pointer to the buffer to receive the converted
+ * string.
+ * @param a_uOutputDataSize Size of the output buffer in char.
+ * @return true if all of the input data, including the
+ * terminating NULL character was successfully
+ * converted.
+ */
+ bool ConvertToStore(
+ const SI_CHAR * a_pInputData,
+ char * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ // calc input string length (SI_CHAR type and size independent)
+ size_t uInputLen = strlen((const char *)a_pInputData) + 1;
+ if (uInputLen > a_uOutputDataSize) {
+ return false;
+ }
+
+ // ascii/UTF-8 needs no conversion
+ memcpy(a_pOutputData, a_pInputData, uInputLen);
+ return true;
+ }
+};
+
+
+// ---------------------------------------------------------------------------
+// SI_CONVERT_GENERIC
+// ---------------------------------------------------------------------------
+#ifdef SI_CONVERT_GENERIC
+
+#define SI_Case SI_GenericCase
+#define SI_NoCase SI_GenericNoCase
+
+#include <wchar.h>
+#include "ConvertUTF.h"
+
+/**
+ * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference
+ * library functions. This can be used on all platforms.
+ */
+template<class SI_CHAR>
+class SI_ConvertW {
+ bool m_bStoreIsUtf8;
+protected:
+ SI_ConvertW() { }
+public:
+ SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
+
+ /* copy and assignment */
+ SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
+ SI_ConvertW & operator=(const SI_ConvertW & rhs) {
+ m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
+ return *this;
+ }
+
+ /** Calculate the number of SI_CHAR required for converting the input
+ * from the storage format. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @return Number of SI_CHAR required by the string when
+ * converted. If there are embedded NULL bytes in the
+ * input data, only the string up and not including
+ * the NULL byte will be converted.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen)
+ {
+ SI_ASSERT(a_uInputDataLen != (size_t) -1);
+
+ if (m_bStoreIsUtf8) {
+ // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
+ // so we just return the same number of characters required as for
+ // the source text.
+ return a_uInputDataLen;
+ }
+ else {
+ return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
+ }
+ }
+
+ /** Convert the input string from the storage format to SI_CHAR.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @param a_pOutputData Pointer to the output buffer to received the
+ * converted data.
+ * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
+ * @return true if all of the input data was successfully
+ * converted.
+ */
+ bool ConvertFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen,
+ SI_CHAR * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ if (m_bStoreIsUtf8) {
+ // This uses the Unicode reference implementation to do the
+ // conversion from UTF-8 to wchar_t. The required files are
+ // ConvertUTF.h and ConvertUTF.c which should be included in
+ // the distribution but are publically available from unicode.org
+ // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
+ ConversionResult retval;
+ const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
+ if (sizeof(wchar_t) == sizeof(UTF32)) {
+ UTF32 * pUtf32 = (UTF32 *) a_pOutputData;
+ retval = ConvertUTF8toUTF32(
+ &pUtf8, pUtf8 + a_uInputDataLen,
+ &pUtf32, pUtf32 + a_uOutputDataSize,
+ lenientConversion);
+ }
+ else if (sizeof(wchar_t) == sizeof(UTF16)) {
+ UTF16 * pUtf16 = (UTF16 *) a_pOutputData;
+ retval = ConvertUTF8toUTF16(
+ &pUtf8, pUtf8 + a_uInputDataLen,
+ &pUtf16, pUtf16 + a_uOutputDataSize,
+ lenientConversion);
+ }
+ return retval == conversionOK;
+ }
+ else {
+ size_t retval = mbstowcs(a_pOutputData,
+ a_pInputData, a_uOutputDataSize);
+ return retval != (size_t)(-1);
+ }
+ }
+
+ /** Calculate the number of char required by the storage format of this
+ * data. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated string to calculate the number of
+ * bytes required to be converted to storage format.
+ * @return Number of bytes required by the string when
+ * converted to storage format. This size always
+ * includes space for the terminating NULL character.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeToStore(
+ const SI_CHAR * a_pInputData)
+ {
+ if (m_bStoreIsUtf8) {
+ // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
+ size_t uLen = 0;
+ while (a_pInputData[uLen]) {
+ ++uLen;
+ }
+ return (6 * uLen) + 1;
+ }
+ else {
+ size_t uLen = wcstombs(NULL, a_pInputData, 0);
+ if (uLen == (size_t)(-1)) {
+ return uLen;
+ }
+ return uLen + 1; // include NULL terminator
+ }
+ }
+
+ /** Convert the input string to the storage format of this data.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated source string to convert. All of
+ * the data will be converted including the
+ * terminating NULL character.
+ * @param a_pOutputData Pointer to the buffer to receive the converted
+ * string.
+ * @param a_uOutputDataSize Size of the output buffer in char.
+ * @return true if all of the input data, including the
+ * terminating NULL character was successfully
+ * converted.
+ */
+ bool ConvertToStore(
+ const SI_CHAR * a_pInputData,
+ char * a_pOutputData,
+ size_t a_uOutputDataSize
+ )
+ {
+ if (m_bStoreIsUtf8) {
+ // calc input string length (SI_CHAR type and size independent)
+ size_t uInputLen = 0;
+ while (a_pInputData[uInputLen]) {
+ ++uInputLen;
+ }
+ ++uInputLen; // include the NULL char
+
+ // This uses the Unicode reference implementation to do the
+ // conversion from wchar_t to UTF-8. The required files are
+ // ConvertUTF.h and ConvertUTF.c which should be included in
+ // the distribution but are publically available from unicode.org
+ // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
+ ConversionResult retval;
+ UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
+ if (sizeof(wchar_t) == sizeof(UTF32)) {
+ const UTF32 * pUtf32 = (const UTF32 *) a_pInputData;
+ retval = ConvertUTF32toUTF8(
+ &pUtf32, pUtf32 + uInputLen,
+ &pUtf8, pUtf8 + a_uOutputDataSize,
+ lenientConversion);
+ }
+ else if (sizeof(wchar_t) == sizeof(UTF16)) {
+ const UTF16 * pUtf16 = (const UTF16 *) a_pInputData;
+ retval = ConvertUTF16toUTF8(
+ &pUtf16, pUtf16 + uInputLen,
+ &pUtf8, pUtf8 + a_uOutputDataSize,
+ lenientConversion);
+ }
+ return retval == conversionOK;
+ }
+ else {
+ size_t retval = wcstombs(a_pOutputData,
+ a_pInputData, a_uOutputDataSize);
+ return retval != (size_t) -1;
+ }
+ }
+};
+
+#endif // SI_CONVERT_GENERIC
+
+
+// ---------------------------------------------------------------------------
+// SI_CONVERT_ICU
+// ---------------------------------------------------------------------------
+#ifdef SI_CONVERT_ICU
+
+#define SI_Case SI_GenericCase
+#define SI_NoCase SI_GenericNoCase
+
+#include <unicode/ucnv.h>
+
+/**
+ * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms.
+ */
+template<class SI_CHAR>
+class SI_ConvertW {
+ const char * m_pEncoding;
+ UConverter * m_pConverter;
+protected:
+ SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
+public:
+ SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
+ m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
+ }
+
+ /* copy and assignment */
+ SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
+ SI_ConvertW & operator=(const SI_ConvertW & rhs) {
+ m_pEncoding = rhs.m_pEncoding;
+ m_pConverter = NULL;
+ return *this;
+ }
+ ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); }
+
+ /** Calculate the number of UChar required for converting the input
+ * from the storage format. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to UChar.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @return Number of UChar required by the string when
+ * converted. If there are embedded NULL bytes in the
+ * input data, only the string up and not including
+ * the NULL byte will be converted.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen)
+ {
+ SI_ASSERT(a_uInputDataLen != (size_t) -1);
+
+ UErrorCode nError;
+
+ if (!m_pConverter) {
+ nError = U_ZERO_ERROR;
+ m_pConverter = ucnv_open(m_pEncoding, &nError);
+ if (U_FAILURE(nError)) {
+ return (size_t) -1;
+ }
+ }
+
+ nError = U_ZERO_ERROR;
+ int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,
+ a_pInputData, (int32_t) a_uInputDataLen, &nError);
+ if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
+ return (size_t) -1;
+ }
+
+ return (size_t) nLen;
+ }
+
+ /** Convert the input string from the storage format to UChar.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to UChar.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @param a_pOutputData Pointer to the output buffer to received the
+ * converted data.
+ * @param a_uOutputDataSize Size of the output buffer in UChar.
+ * @return true if all of the input data was successfully
+ * converted.
+ */
+ bool ConvertFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen,
+ UChar * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ UErrorCode nError;
+
+ if (!m_pConverter) {
+ nError = U_ZERO_ERROR;
+ m_pConverter = ucnv_open(m_pEncoding, &nError);
+ if (U_FAILURE(nError)) {
+ return false;
+ }
+ }
+
+ nError = U_ZERO_ERROR;
+ ucnv_toUChars(m_pConverter,
+ a_pOutputData, (int32_t) a_uOutputDataSize,
+ a_pInputData, (int32_t) a_uInputDataLen, &nError);
+ if (U_FAILURE(nError)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /** Calculate the number of char required by the storage format of this
+ * data. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated string to calculate the number of
+ * bytes required to be converted to storage format.
+ * @return Number of bytes required by the string when
+ * converted to storage format. This size always
+ * includes space for the terminating NULL character.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeToStore(
+ const UChar * a_pInputData)
+ {
+ UErrorCode nError;
+
+ if (!m_pConverter) {
+ nError = U_ZERO_ERROR;
+ m_pConverter = ucnv_open(m_pEncoding, &nError);
+ if (U_FAILURE(nError)) {
+ return (size_t) -1;
+ }
+ }
+
+ nError = U_ZERO_ERROR;
+ int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,
+ a_pInputData, -1, &nError);
+ if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
+ return (size_t) -1;
+ }
+
+ return (size_t) nLen + 1;
+ }
+
+ /** Convert the input string to the storage format of this data.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated source string to convert. All of
+ * the data will be converted including the
+ * terminating NULL character.
+ * @param a_pOutputData Pointer to the buffer to receive the converted
+ * string.
+ * @param a_pOutputDataSize Size of the output buffer in char.
+ * @return true if all of the input data, including the
+ * terminating NULL character was successfully
+ * converted.
+ */
+ bool ConvertToStore(
+ const UChar * a_pInputData,
+ char * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ UErrorCode nError;
+
+ if (!m_pConverter) {
+ nError = U_ZERO_ERROR;
+ m_pConverter = ucnv_open(m_pEncoding, &nError);
+ if (U_FAILURE(nError)) {
+ return false;
+ }
+ }
+
+ nError = U_ZERO_ERROR;
+ ucnv_fromUChars(m_pConverter,
+ a_pOutputData, (int32_t) a_uOutputDataSize,
+ a_pInputData, -1, &nError);
+ if (U_FAILURE(nError)) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+#endif // SI_CONVERT_ICU
+
+
+// ---------------------------------------------------------------------------
+// SI_CONVERT_WIN32
+// ---------------------------------------------------------------------------
+#ifdef SI_CONVERT_WIN32
+
+#define SI_Case SI_GenericCase
+
+// Windows CE doesn't have errno or MBCS libraries
+#ifdef _WIN32_WCE
+# ifndef SI_NO_MBCS
+# define SI_NO_MBCS
+# endif
+#endif
+
+#include <windows.h>
+#ifdef SI_NO_MBCS
+# define SI_NoCase SI_GenericNoCase
+#else // !SI_NO_MBCS
+/**
+ * Case-insensitive comparison class using Win32 MBCS functions. This class
+ * returns a case-insensitive semi-collation order for MBCS text. It may not
+ * be safe for UTF-8 text returned in char format as we don't know what
+ * characters will be folded by the function! Therefore, if you are using
+ * SI_CHAR == char and SetUnicode(true), then you need to use the generic
+ * SI_NoCase class instead.
+ */
+#include <mbstring.h>
+template<class SI_CHAR>
+struct SI_NoCase {
+ bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
+ if (sizeof(SI_CHAR) == sizeof(char)) {
+ return _mbsicmp((const unsigned char *)pLeft,
+ (const unsigned char *)pRight) < 0;
+ }
+ if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
+ return _wcsicmp((const wchar_t *)pLeft,
+ (const wchar_t *)pRight) < 0;
+ }
+ return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight);
+ }
+};
+#endif // SI_NO_MBCS
+
+/**
+ * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses
+ * only the Win32 functions and doesn't require the external Unicode UTF-8
+ * conversion library. It will not work on Windows 95 without using Microsoft
+ * Layer for Unicode in your application.
+ */
+template<class SI_CHAR>
+class SI_ConvertW {
+ UINT m_uCodePage;
+protected:
+ SI_ConvertW() { }
+public:
+ SI_ConvertW(bool a_bStoreIsUtf8) {
+ m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP;
+ }
+
+ /* copy and assignment */
+ SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
+ SI_ConvertW & operator=(const SI_ConvertW & rhs) {
+ m_uCodePage = rhs.m_uCodePage;
+ return *this;
+ }
+
+ /** Calculate the number of SI_CHAR required for converting the input
+ * from the storage format. The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @return Number of SI_CHAR required by the string when
+ * converted. If there are embedded NULL bytes in the
+ * input data, only the string up and not including
+ * the NULL byte will be converted.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen)
+ {
+ SI_ASSERT(a_uInputDataLen != (size_t) -1);
+
+ int retval = MultiByteToWideChar(
+ m_uCodePage, 0,
+ a_pInputData, (int) a_uInputDataLen,
+ 0, 0);
+ return (size_t)(retval > 0 ? retval : -1);
+ }
+
+ /** Convert the input string from the storage format to SI_CHAR.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData Data in storage format to be converted to SI_CHAR.
+ * @param a_uInputDataLen Length of storage format data in bytes. This
+ * must be the actual length of the data, including
+ * NULL byte if NULL terminated string is required.
+ * @param a_pOutputData Pointer to the output buffer to received the
+ * converted data.
+ * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
+ * @return true if all of the input data was successfully
+ * converted.
+ */
+ bool ConvertFromStore(
+ const char * a_pInputData,
+ size_t a_uInputDataLen,
+ SI_CHAR * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ int nSize = MultiByteToWideChar(
+ m_uCodePage, 0,
+ a_pInputData, (int) a_uInputDataLen,
+ (wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
+ return (nSize > 0);
+ }
+
+ /** Calculate the number of char required by the storage format of this
+ * data. The storage format is always UTF-8.
+ *
+ * @param a_pInputData NULL terminated string to calculate the number of
+ * bytes required to be converted to storage format.
+ * @return Number of bytes required by the string when
+ * converted to storage format. This size always
+ * includes space for the terminating NULL character.
+ * @return -1 cast to size_t on a conversion error.
+ */
+ size_t SizeToStore(
+ const SI_CHAR * a_pInputData)
+ {
+ int retval = WideCharToMultiByte(
+ m_uCodePage, 0,
+ (const wchar_t *) a_pInputData, -1,
+ 0, 0, 0, 0);
+ return (size_t) (retval > 0 ? retval : -1);
+ }
+
+ /** Convert the input string to the storage format of this data.
+ * The storage format is always UTF-8 or MBCS.
+ *
+ * @param a_pInputData NULL terminated source string to convert. All of
+ * the data will be converted including the
+ * terminating NULL character.
+ * @param a_pOutputData Pointer to the buffer to receive the converted
+ * string.
+ * @param a_pOutputDataSize Size of the output buffer in char.
+ * @return true if all of the input data, including the
+ * terminating NULL character was successfully
+ * converted.
+ */
+ bool ConvertToStore(
+ const SI_CHAR * a_pInputData,
+ char * a_pOutputData,
+ size_t a_uOutputDataSize)
+ {
+ int retval = WideCharToMultiByte(
+ m_uCodePage, 0,
+ (const wchar_t *) a_pInputData, -1,
+ a_pOutputData, (int) a_uOutputDataSize, 0, 0);
+ return retval > 0;
+ }
+};
+
+#endif // SI_CONVERT_WIN32
+
+
+// ---------------------------------------------------------------------------
+// TYPE DEFINITIONS
+// ---------------------------------------------------------------------------
+
+typedef CSimpleIniTempl<char,
+ SI_NoCase<char>,SI_ConvertA<char> > CSimpleIniA;
+typedef CSimpleIniTempl<char,
+ SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA;
+
+#if defined(SI_CONVERT_ICU)
+typedef CSimpleIniTempl<UChar,
+ SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;
+typedef CSimpleIniTempl<UChar,
+ SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;
+#else
+typedef CSimpleIniTempl<wchar_t,
+ SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;
+typedef CSimpleIniTempl<wchar_t,
+ SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;
+#endif
+
+#ifdef _UNICODE
+# define CSimpleIni CSimpleIniW
+# define CSimpleIniCase CSimpleIniCaseW
+# define SI_NEWLINE SI_NEWLINE_W
+#else // !_UNICODE
+# define CSimpleIni CSimpleIniA
+# define CSimpleIniCase CSimpleIniCaseA
+# define SI_NEWLINE SI_NEWLINE_A
+#endif // _UNICODE
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif // INCLUDED_SimpleIni_h
+
diff --git a/lib/simpleini/SimpleIni.sln b/lib/simpleini/SimpleIni.sln
new file mode 100644
index 0000000..6fd8a73
--- /dev/null
+++ b/lib/simpleini/SimpleIni.sln
@@ -0,0 +1,29 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleIni", "SimpleIni.vcproj", "{C23240A6-AA9D-4827-AF06-C98E97CA6DFB}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ Debug = Debug
+ Debug Unicode = Debug Unicode
+ Release = Release
+ Release Unicode = Release Unicode
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Debug.ActiveCfg = Debug|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Debug.Build.0 = Debug|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Debug Unicode.ActiveCfg = Debug Unicode|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Debug Unicode.Build.0 = Debug Unicode|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Release.ActiveCfg = Release|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Release.Build.0 = Release|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Release Unicode.ActiveCfg = Release Unicode|Win32
+ {C23240A6-AA9D-4827-AF06-C98E97CA6DFB}.Release Unicode.Build.0 = Release Unicode|Win32
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/lib/simpleini/SimpleIni.vcproj b/lib/simpleini/SimpleIni.vcproj
new file mode 100644
index 0000000..e5ee0db
--- /dev/null
+++ b/lib/simpleini/SimpleIni.vcproj
@@ -0,0 +1,291 @@
+<?xml version="1.0" encoding="shift_jis"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="SimpleIni"
+ ProjectGUID="{C23240A6-AA9D-4827-AF06-C98E97CA6DFB}"
+ RootNamespace="SimpleIni"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Debug"
+ IntermediateDirectory="Debug"
+ ConfigurationType="1"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="5"
+ UsePrecompiledHeader="0"
+ WarningLevel="4"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/testsi.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/SimpleIni.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Release"
+ IntermediateDirectory="Release"
+ ConfigurationType="1"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="4"
+ UsePrecompiledHeader="0"
+ WarningLevel="4"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/testsi.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="TRUE"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Debug Unicode|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="5"
+ UsePrecompiledHeader="0"
+ WarningLevel="4"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/testsi.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/SimpleIni.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release Unicode|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1">
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="4"
+ UsePrecompiledHeader="0"
+ WarningLevel="4"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/testsi.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="TRUE"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+ <File
+ RelativePath=".\snippets.cpp">
+ </File>
+ <File
+ RelativePath=".\test1.cpp">
+ </File>
+ <File
+ RelativePath=".\testsi.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Library Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+ <File
+ RelativePath=".\SimpleIni.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
+ <File
+ RelativePath=".\Makefile">
+ </File>
+ <File
+ RelativePath=".\testsi-EUCJP.ini">
+ </File>
+ <File
+ RelativePath=".\testsi-SJIS.ini">
+ </File>
+ <File
+ RelativePath=".\testsi-UTF8.ini">
+ </File>
+ </Filter>
+ <Filter
+ Name="Conversion Files"
+ Filter="">
+ <File
+ RelativePath=".\ConvertUTF.c">
+ </File>
+ <File
+ RelativePath=".\ConvertUTF.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Documentation"
+ Filter="">
+ <File
+ RelativePath=".\simpleini.doxy">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="if not exist &quot;C:\Program Files\doxygen\bin\doxygen.exe&quot; goto done
+
+echo Generating documentation...
+&quot;C:\Program Files\doxygen\bin\doxygen.exe&quot; $(InputDir)simpleini.doxy
+
+:done
+"
+ Outputs="d:\src\simpleini-doc\html\index.html"/>
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Tests"
+ Filter="">
+ <File
+ RelativePath=".\test1-expected.ini">
+ </File>
+ <File
+ RelativePath=".\test1-input.ini">
+ </File>
+ <File
+ RelativePath=".\test1-output.ini">
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/lib/simpleini/ini.syn b/lib/simpleini/ini.syn
new file mode 100644
index 0000000..718c560
--- /dev/null
+++ b/lib/simpleini/ini.syn
@@ -0,0 +1,36 @@
+; Syntax file for ini files - contributed by Brodie Thiesfield
+;
+; Suggested Colors:
+; Comments (;#) Comments, Comments 2 Green
+; Sections Characters Red
+; Values Strings Blue
+
+C=1
+
+[Syntax]
+Namespace1 = 6
+IgnoreCase = Yes
+KeyWordLength = 1
+BracketChars =
+OperatorChars =
+PreprocStart =
+SyntaxStart =
+SyntaxEnd =
+HexPrefix =
+CommentStart =
+CommentEnd =
+CommentStartAlt =
+CommentEndAlt =
+SingleComment = #
+SingleCommentCol =
+SingleCommentAlt = ;
+SingleCommentColAlt =
+SingleCommentEsc =
+StringsSpanLines = No
+StringStart =
+StringEnd =
+StringAlt = =
+StringEsc =
+CharStart = [
+CharEnd = ]
+CharEsc =
diff --git a/lib/simpleini/package.cmd b/lib/simpleini/package.cmd
new file mode 100644
index 0000000..2ae29db
--- /dev/null
+++ b/lib/simpleini/package.cmd
@@ -0,0 +1,26 @@
+set VERSION=4.15
+
+set SEVENZIP="C:\Program Files\7-Zip\7z.exe"
+
+FOR /F "tokens=*" %%G IN ('DIR /AD /B /S Debug*') DO (
+ DEL /S /Q "%%G"
+ RD "%%G"
+)
+FOR /F "tokens=*" %%G IN ('DIR /AD /B /S Release*') DO (
+ DEL /S /Q "%%G"
+ RD "%%G"
+)
+DEL /Q "SimpleIni.ncb"
+ATTRIB -H "SimpleIni.suo"
+DEL /Q "SimpleIni.suo"
+DEL /Q "SimpleIni.opt"
+DEL /Q testsi-out*.ini
+DEL /Q test1-blah.ini
+DEL /Q test1-output.ini
+START "Generate documentation" /WAIT "C:\Program Files (x86)\doxygen\bin\doxygen.exe" SimpleIni.doxy
+cd ..
+del simpleini-%VERSION%.zip
+%SEVENZIP% a -tzip -r- -x!simpleini\.svn simpleini-%VERSION%.zip simpleini\*
+del simpleini-doc.zip
+%SEVENZIP% a -tzip -r simpleini-doc.zip simpleini-doc\*
+cd simpleini
diff --git a/lib/simpleini/simpleini.doxy b/lib/simpleini/simpleini.doxy
new file mode 100644
index 0000000..6a27221
--- /dev/null
+++ b/lib/simpleini/simpleini.doxy
@@ -0,0 +1,1321 @@
+# Doxyfile 1.5.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file that
+# follow. The default is UTF-8 which is also the encoding used for all text before
+# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into
+# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of
+# possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = SimpleIni
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = D:/src/simpleini-doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
+# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
+# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
+# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF = "The $name class " \
+ "The $name widget " \
+ "The $name file " \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH = "D:/src/simpleini/ "
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
+# include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct (or union) is
+# documented as struct with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code where the coding convention is that all structs are
+# typedef'ed and only the typedef is referenced never the struct's name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be extracted
+# and appear in the documentation as a namespace called 'anonymous_namespace{file}',
+# where file will be replaced with the base name of the file that contains the anonymous
+# namespace. By default anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file($line) : $text "
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = D:/src/simpleini/SimpleIni.h
+
+# This tag can be used to specify the character encoding of the source files that
+# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default
+# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding.
+# See http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the output.
+# The symbol name can be a fully qualified name, a word, or if the wildcard * is used,
+# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH
+# then you must also enable this option. If you don't then doxygen will produce
+# a warning and turn it on anyway
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = SI_HAS_WIDE_FILE \
+ SI_SUPPORT_IOSTREAMS
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to
+# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to
+# specify the directory where the mscgen tool resides. If left empty the tool is assumed to
+# be found in the default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will
+# generate a caller dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the number
+# of direct children of the root node in a graph is already larger than
+# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 1000
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/lib/simpleini/simpleini.dsp b/lib/simpleini/simpleini.dsp
new file mode 100644
index 0000000..97c386a
--- /dev/null
+++ b/lib/simpleini/simpleini.dsp
@@ -0,0 +1,178 @@
+# Microsoft Developer Studio Project File - Name="simpleini" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=simpleini - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "simpleini.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "simpleini.mak" CFG="simpleini - Win32 Debug Unicode"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "simpleini - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "simpleini - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "simpleini - Win32 Debug Unicode" (based on "Win32 (x86) Console Application")
+!MESSAGE "simpleini - Win32 Release Unicode" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "simpleini - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0xc09 /d "NDEBUG"
+# ADD RSC /l 0xc09 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"Release/testsi.exe"
+
+!ELSEIF "$(CFG)" == "simpleini - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "simpleini___Win32_Debug"
+# PROP BASE Intermediate_Dir "simpleini___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c
+# SUBTRACT CPP /Fr /YX
+# ADD BASE RSC /l 0xc09 /d "_DEBUG"
+# ADD RSC /l 0xc09 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug/testsi.exe" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "simpleini - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug Unicode"
+# PROP BASE Intermediate_Dir "Debug Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug Unicode"
+# PROP Intermediate_Dir "Debug Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /D "SI_USE_GENERIC_CONVERSION" /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0xc09 /d "_DEBUG"
+# ADD RSC /l 0xc09 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug Unicode/testsi.exe" /pdbtype:sept
+
+!ELSEIF "$(CFG)" == "simpleini - Win32 Release Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release Unicode"
+# PROP BASE Intermediate_Dir "Release Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release Unicode"
+# PROP Intermediate_Dir "Release Unicode"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /D "SI_USE_GENERIC_CONVERSION" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0xc09 /d "NDEBUG"
+# ADD RSC /l 0xc09 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"Release Unicode/testsi.exe"
+
+!ENDIF
+
+# Begin Target
+
+# Name "simpleini - Win32 Release"
+# Name "simpleini - Win32 Debug"
+# Name "simpleini - Win32 Debug Unicode"
+# Name "simpleini - Win32 Release Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\snippets.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\test1.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\testsi.cpp
+# End Source File
+# End Group
+# Begin Group "Library Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\SimpleIni.h
+# End Source File
+# End Group
+# Begin Group "Generic Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\ConvertUTF.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConvertUTF.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/lib/simpleini/simpleini.dsw b/lib/simpleini/simpleini.dsw
new file mode 100644
index 0000000..2d593ed
--- /dev/null
+++ b/lib/simpleini/simpleini.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "simpleini"=.\simpleini.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/lib/simpleini/snippets.cpp b/lib/simpleini/snippets.cpp
new file mode 100644
index 0000000..646f4ff
--- /dev/null
+++ b/lib/simpleini/snippets.cpp
@@ -0,0 +1,123 @@
+// File: snippets.cpp
+// Library: SimpleIni
+// Author: Brodie Thiesfield <code@jellycan.com>
+// Source: http://code.jellycan.com/simpleini/
+//
+// Snippets that are used on the website
+
+#ifdef _WIN32
+# pragma warning(disable: 4786)
+#endif
+
+#ifndef _WIN32
+# include <unistd.h>
+#endif
+#include <fstream>
+
+#define SI_SUPPORT_IOSTREAMS
+#include "SimpleIni.h"
+
+bool
+snippets(
+ const char * a_pszFile,
+ bool a_bIsUtf8,
+ bool a_bUseMultiKey,
+ bool a_bUseMultiLine
+ )
+{
+ // LOADING DATA
+
+ // load from a data file
+ CSimpleIniA ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine);
+ SI_Error rc = ini.LoadFile(a_pszFile);
+ if (rc < 0) return false;
+
+ // load from a string
+ std::string strData;
+ rc = ini.LoadData(strData.c_str(), strData.size());
+ if (rc < 0) return false;
+
+ // GETTING SECTIONS AND KEYS
+
+ // get all sections
+ CSimpleIniA::TNamesDepend sections;
+ ini.GetAllSections(sections);
+
+ // get all keys in a section
+ CSimpleIniA::TNamesDepend keys;
+ ini.GetAllKeys("section-name", keys);
+
+ // GETTING VALUES
+
+ // get the value of a key
+ const char * pszValue = ini.GetValue("section-name",
+ "key-name", NULL /*default*/);
+
+ // get the value of a key which may have multiple
+ // values. If bHasMultipleValues is true, then just
+ // one value has been returned
+ bool bHasMultipleValues;
+ pszValue = ini.GetValue("section-name", "key-name",
+ NULL /*default*/, &bHasMultipleValues);
+
+ // get all values of a key with multiple values
+ CSimpleIniA::TNamesDepend values;
+ ini.GetAllValues("section-name", "key-name", values);
+
+ // sort the values into the original load order
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+ /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */
+ values.sort();
+#else
+ values.sort(CSimpleIniA::Entry::LoadOrder());
+#endif
+
+ // output all of the items
+ CSimpleIniA::TNamesDepend::const_iterator i;
+ for (i = values.begin(); i != values.end(); ++i) {
+ printf("key-name = '%s'\n", i->pItem);
+ }
+
+ // MODIFYING DATA
+
+ // adding a new section
+ rc = ini.SetValue("new-section", NULL, NULL);
+ if (rc < 0) return false;
+ printf("section: %s\n", rc == SI_INSERTED ?
+ "inserted" : "updated");
+
+ // adding a new key ("new-section" will be added
+ // automatically if it doesn't already exist.
+ rc = ini.SetValue("new-section", "new-key", "value");
+ if (rc < 0) return false;
+ printf("key: %s\n", rc == SI_INSERTED ?
+ "inserted" : "updated");
+
+ // changing the value of a key
+ rc = ini.SetValue("section", "key", "updated-value");
+ if (rc < 0) return false;
+ printf("key: %s\n", rc == SI_INSERTED ?
+ "inserted" : "updated");
+
+ // DELETING DATA
+
+ // deleting a key from a section. Optionally the entire
+ // section may be deleted if it is now empty.
+ ini.Delete("section-name", "key-name",
+ true /*delete the section if empty*/);
+
+ // deleting an entire section and all keys in it
+ ini.Delete("section-name", NULL);
+
+ // SAVING DATA
+
+ // save the data to a string
+ rc = ini.Save(strData);
+ if (rc < 0) return false;
+
+ // save the data back to the file
+ rc = ini.SaveFile(a_pszFile);
+ if (rc < 0) return false;
+
+ return true;
+}
diff --git a/lib/simpleini/test.cmd b/lib/simpleini/test.cmd
new file mode 100644
index 0000000..f5d64ea
--- /dev/null
+++ b/lib/simpleini/test.cmd
@@ -0,0 +1,24 @@
+@echo off
+
+Debug\testsi.exe -u -m -l test1-input.ini > test1-blah.ini
+fc test1-expected.ini test1-output.ini
+if errorlevel 1 goto error
+
+"Debug Unicode\testsi.exe" -u -m -l test1-input.ini > test1-blah.ini
+fc test1-expected.ini test1-output.ini
+if errorlevel 1 goto error
+
+Release\testsi.exe -u -m -l test1-input.ini > test1-blah.ini
+fc test1-expected.ini test1-output.ini
+if errorlevel 1 goto error
+
+"Release Unicode\testsi.exe" -u -m -l test1-input.ini > test1-blah.ini
+fc test1-expected.ini test1-output.ini
+if errorlevel 1 goto error
+
+exit /b 0
+
+:error
+echo Failed during test run. Output file doesn't match expected file.
+pause
+exit /b 1
diff --git a/lib/simpleini/test1-expected.ini b/lib/simpleini/test1-expected.ini
new file mode 100644
index 0000000..6b26c6c
--- /dev/null
+++ b/lib/simpleini/test1-expected.ini
@@ -0,0 +1,85 @@
+; testsi-UTF8-std.ini : standard UTF-8 test file for SimpleIni automated testing
+;
+; The number after a section or key is the order that it is defined in this file
+; to make it easier to see if it has been written out correctly. This file should
+; be loaded with Unicode / MultiKey / MultiLine turned on.
+
+
+
+; This comment should be joined on to the one below it about the key
+; with no section.
+
+; Key with no section
+lonely-key = nosection
+another = nosection either
+
+; This key has no value
+empty =
+
+
+; This should be joined with the comment below about japanese.
+; Another line which will be un-indented.
+
+; This is a section of keys showing the word Japanese in different syllabies.
+[ordered-1]
+a-1 = blah
+
+; this is in kanji
+japanese-2 = 日本語
+
+; this is in hiragana
+japanese-3 = にほんご
+
+; this is in katakana
+japanese-4 = ニホンゴ
+
+; this is in romaji
+japanese-5 = nihongo
+
+; kanji as the key
+日本語-6 = japanese
+
+
+[multi-2]
+
+; value a
+test = a
+
+; value b
+test = b
+
+; value c
+test = c
+
+; value d
+test = d
+
+
+[multiline-3]
+
+; This is obviously a multi-line entry
+multiline-1 = <<<END_OF_TEXT
+
+This is a multi-line comment. It
+will continue until we have the word MULTI
+on a line by itself.
+
+日本語も。
+
+END_OF_TEXT
+
+; This looks like multi-line, but because the newline following the last
+; line is discarded, it will be converted into a single line entry.
+another-2 = This is not a multiline entry.
+
+; If you wanted a multiline entry with a single line, you need to add
+; an extra line to it.
+another-3 = <<<END_OF_TEXT
+This is a multiline entry.
+
+END_OF_TEXT
+
+
+[integer]
+dec = 42
+hex = 0x2a
diff --git a/lib/simpleini/test1-input.ini b/lib/simpleini/test1-input.ini
new file mode 100644
index 0000000..76d16dd
--- /dev/null
+++ b/lib/simpleini/test1-input.ini
@@ -0,0 +1,76 @@
+; testsi-UTF8-std.ini : standard UTF-8 test file for SimpleIni automated testing
+;
+; The number after a section or key is the order that it is defined in this file
+; to make it easier to see if it has been written out correctly. This file should
+; be loaded with Unicode / MultiKey / MultiLine turned on.
+
+; This comment should be joined on to the one below it about the key
+; with no section.
+
+; Key with no section
+lonely-key = nosection
+another = nosection either
+
+; This key has no value
+empty =
+
+; This should be joined with the comment below about japanese.
+ ; Another line which will be un-indented.
+
+; This is a section of keys showing the word Japanese in different syllabies.
+[ordered-1]
+a-1 = blah
+
+; this is in kanji
+japanese-2 = 日本語
+
+; this is in hiragana
+japanese-3 = にほんご
+
+; this is in katakana
+japanese-4 = ニホンゴ
+
+; this is in romaji
+japanese-5 = nihongo
+
+; kanji as the key
+日本語-6 = japanese
+
+[multi-2]
+; value a
+test = a
+; value b
+test = b
+; value c
+test = c
+; value d
+test = d
+
+[multiline-3]
+; This is obviously a multi-line entry
+multiline-1 = <<<MULTI
+
+This is a multi-line comment. It
+will continue until we have the word MULTI
+on a line by itself.
+
+日本語も。
+
+MULTI
+
+; This looks like multi-line, but because the newline following the last
+; line is discarded, it will be converted into a single line entry.
+another-2 = <<<MULTI
+This is not a multiline entry.
+MULTI
+
+; If you wanted a multiline entry with a single line, you need to add
+; an extra line to it.
+another-3 = <<<MULTI
+This is a multiline entry.
+
+MULTI
+
+[integer]
+dec = 42
+hex = 0x2a
diff --git a/lib/simpleini/test1.cpp b/lib/simpleini/test1.cpp
new file mode 100644
index 0000000..b00d38c
--- /dev/null
+++ b/lib/simpleini/test1.cpp
@@ -0,0 +1,166 @@
+// File: test1.cpp
+// Library: SimpleIni
+// Author: Brodie Thiesfield <code@jellycan.com>
+// Source: http://code.jellycan.com/simpleini/
+//
+// Automated testing for SimpleIni streams
+
+#ifdef _WIN32
+# pragma warning(disable: 4786)
+#endif
+
+#ifdef _WIN32
+# include <windows.h>
+# define DELETE_FILE DeleteFileA
+#else
+# include <unistd.h>
+# define DELETE_FILE unlink
+#endif
+#include <fstream>
+
+#define SI_SUPPORT_IOSTREAMS
+#include "SimpleIni.h"
+
+class Test
+{
+ std::string m_strTest;
+
+public:
+ Test(const char * a_pszName)
+ : m_strTest(a_pszName)
+ {
+ printf("%s: test starting\n", m_strTest.c_str());
+ }
+
+ bool Success()
+ {
+ printf("%s: test succeeded\n", m_strTest.c_str());
+ return false;
+ }
+
+ bool Failure(const char * pszReason)
+ {
+ printf("%s: test FAILED (%s)\n", m_strTest.c_str(), pszReason);
+ return false;
+ }
+};
+
+bool FileComparisonTest(const char * a_pszFile1, const char * a_pszFile2) {
+ // ensure that the two files are the same
+ try {
+ std::string strFile1, strFile2;
+
+ char szBuf[1024];
+ FILE * fp = NULL;
+
+#if __STDC_WANT_SECURE_LIB__
+ fopen_s(&fp, a_pszFile1, "rb");
+#else
+ fp = fopen(a_pszFile1, "rb");
+#endif
+ if (!fp) throw false;
+ while (!feof(fp)) {
+ size_t n = fread(szBuf, 1, sizeof(szBuf), fp);
+ strFile1.append(szBuf, n);
+ }
+ fclose(fp);
+
+ fp = NULL;
+#if __STDC_WANT_SECURE_LIB__
+ fopen_s(&fp, a_pszFile2, "rb");
+#else
+ fp = fopen(a_pszFile2, "rb");
+#endif
+ if (!fp) throw false;
+ while (!feof(fp)) {
+ size_t n = fread(szBuf, 1, sizeof(szBuf), fp);
+ strFile2.append(szBuf, n);
+ }
+ fclose(fp);
+
+ if (strFile1 != strFile2) throw false;
+ }
+ catch (...) {
+ return false;
+ }
+
+ return true;
+}
+
+bool FileLoadTest(const char * a_pszFile1, const char * a_pszFile2) {
+ // ensure that the two files load into simpleini the same
+ CSimpleIniA ini(true, true, true);
+ bool b;
+ try {
+ ini.Reset();
+ if (ini.LoadFile(a_pszFile1) < 0) throw "Load failed for file 1";
+ if (ini.SaveFile("test1.ini") < 0) throw "Save failed for file 1";
+
+ ini.Reset();
+ if (ini.LoadFile(a_pszFile2) < 0) throw "Load failed for file 2";
+ if (ini.SaveFile("test2.ini") < 0) throw "Save failed for file 2";
+
+ b = FileComparisonTest("test1.ini", "test2.ini");
+ DELETE_FILE("test1.ini");
+ DELETE_FILE("test2.ini");
+ if (!b) throw "File comparison failed in FileLoadTest";
+ }
+ catch (...) {
+ return false;
+ }
+
+ return true;
+}
+
+bool TestStreams()
+{
+ const char * rgszTestFile[3] = {
+ "test1-input.ini",
+ "test1-output.ini",
+ "test1-expected.ini"
+ };
+
+ Test oTest("TestStreams");
+
+ CSimpleIniW ini;
+ ini.SetUnicode(true);
+ ini.SetMultiKey(true);
+ ini.SetMultiLine(true);
+
+ // load the file
+ try {
+ std::ifstream instream;
+ instream.open(rgszTestFile[0], std::ifstream::in | std::ifstream::binary);
+ if (ini.LoadData(instream) < 0) throw false;
+ instream.close();
+ }
+ catch (...) {
+ return oTest.Failure("Failed to load file");
+ }
+
+ // standard contents test
+ //if (!StandardContentsTest(ini, oTest)) {
+ // return false;
+ //}
+
+ // save the file
+ try {
+ std::ofstream outfile;
+ outfile.open(rgszTestFile[1], std::ofstream::out | std::ofstream::binary);
+ if (ini.Save(outfile, true) < 0) throw false;
+ outfile.close();
+ }
+ catch (...) {
+ return oTest.Failure("Failed to save file");
+ }
+
+ // file comparison test
+ if (!FileComparisonTest(rgszTestFile[1], rgszTestFile[2])) {
+ return oTest.Failure("Failed file comparison");
+ }
+ if (!FileLoadTest(rgszTestFile[1], rgszTestFile[2])) {
+ return oTest.Failure("Failed file load comparison");
+ }
+
+ return oTest.Success();
+}
diff --git a/lib/simpleini/testsi-EUCJP.ini b/lib/simpleini/testsi-EUCJP.ini
new file mode 100644
index 0000000..ee9e987
--- /dev/null
+++ b/lib/simpleini/testsi-EUCJP.ini
@@ -0,0 +1,52 @@
+; test file for SimpleIni
+
+nosection=ok
+NOSECTION=still ok
+ whitespace = ok
+
+[standard]
+foo=foo1
+standard-1=foo
+ܸ=ok1
+
+[Standard]
+Foo=foo2
+standard-2=foo
+ܸ=ok2
+
+ [ Whitespace ]
+
+a=
+
+[ whitespace in section name ]
+ whitespace in key name = whitespace in value name
+
+; comments
+ ; more comments
+
+invalid
+=invalid
+====invalid
+
+[Japanese]
+nihongo = ܸ
+ܸ = ܸ
+
+[ܸ]
+nihongo = ܸ
+ܸ = ܸ
+
+[]
+more=no section name
+
+
+
+[MultiLine]
+single = This is a single line.
+multi = <<<MULTI
+
+This is a multi-line value. It continues until the MULTI tag is found
+on a line by itself with no whitespace before or after it. This value
+will be returned to the user with all newlines and whitespace.
+
+MULTI
diff --git a/lib/simpleini/testsi-SJIS.ini b/lib/simpleini/testsi-SJIS.ini
new file mode 100644
index 0000000..f723ac6
--- /dev/null
+++ b/lib/simpleini/testsi-SJIS.ini
@@ -0,0 +1,51 @@
+; test file for SimpleIni
+
+nosection=ok
+NOSECTION=still ok
+ whitespace = ok
+
+[standard]
+foo=foo1
+standard-1=foo
+{=ok1
+
+[Standard]
+Foo=foo2
+standard-2=foo
+{=ok2
+
+ [ Whitespace ]
+
+a=
+
+[ whitespace in section name ]
+ whitespace in key name = whitespace in value name
+
+; comments
+ ; more comments
+
+invalid
+=invalid
+====invalid
+
+[Japanese]
+nihongo = {
+{ = {
+
+[{]
+nihongo = {
+{ = {
+
+[]
+more=no section name
+
+
+[MultiLine]
+single = This is a single line.
+multi = <<<MULTI
+
+This is a multi-line value. It continues until the MULTI tag is found
+on a line by itself with no whitespace before or after it. This value
+will be returned to the user with all newlines and whitespace.
+
+MULTI
diff --git a/lib/simpleini/testsi-UTF8.ini b/lib/simpleini/testsi-UTF8.ini
new file mode 100644
index 0000000..37d8e6d
--- /dev/null
+++ b/lib/simpleini/testsi-UTF8.ini
@@ -0,0 +1,50 @@
+; test file for SimpleIni
+
+ whitespace = ok
+nosection=ok
+NOSECTION=still ok
+
+[standard]
+foo=foo1
+standard-1=foo
+日本語=ok1
+
+[Standard]
+Foo=foo2
+standard-2=foo
+日本語=ok2
+
+ [ Whitespace ]
+
+a=
+
+[ whitespace in section name ]
+ whitespace in key name = whitespace in value name
+
+; comments
+ ; more comments
+
+invalid
+=invalid
+====invalid
+
+[Japanese]
+nihongo = 日本語
+日本語 = 日本語
+
+[日本語]
+nihongo = 日本語
+日本語 = 日本語
+
+[]
+more=no section name
+
+[MultiLine]
+single = This is a single line.
+multi = <<<MULTI
+
+This is a multi-line value. It continues until the MULTI tag is found
+on a line by itself with no whitespace before or after it. This value
+will be returned to the user with all newlines and whitespace.
+
+MULTI
diff --git a/lib/simpleini/testsi.cpp b/lib/simpleini/testsi.cpp
new file mode 100644
index 0000000..4d6dd9d
--- /dev/null
+++ b/lib/simpleini/testsi.cpp
@@ -0,0 +1,309 @@
+// File: testsi.cpp
+// Library: SimpleIni
+// Author: Brodie Thiesfield <code@jellycan.com>
+// Source: http://code.jellycan.com/simpleini/
+//
+// Demo of usage
+
+#ifdef _WIN32
+# pragma warning(disable: 4786)
+#endif
+
+#include <locale.h>
+#include <stdio.h>
+#include <cassert>
+
+#define SI_SUPPORT_IOSTREAMS
+#if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE)
+# include <fstream>
+#endif
+
+//#define SI_CONVERT_GENERIC
+//#define SI_CONVERT_ICU
+//#define SI_CONVERT_WIN32
+#include "SimpleIni.h"
+
+#ifdef SI_CONVERT_ICU
+// if converting using ICU then we need the ICU library
+# pragma comment(lib, "icuuc.lib")
+#endif
+
+#ifdef _WIN32
+# include <tchar.h>
+#else // !_WIN32
+# define TCHAR char
+# define _T(x) x
+# define _tprintf printf
+# define _tmain main
+#endif // _WIN32
+
+static void
+Test(
+ CSimpleIni & ini
+ )
+{
+ const TCHAR *pszSection = 0;
+ const TCHAR *pItem = 0;
+ const TCHAR *pszVal = 0;
+
+ // get the value of the key "foo" in section "standard"
+ bool bHasMulti;
+ pszVal = ini.GetValue(_T("standard"), _T("foo"), 0, &bHasMulti);
+ _tprintf(_T("\n-- Value of standard::foo is '%s' (hasMulti = %d)\n"),
+ pszVal ? pszVal : _T("(null)"), bHasMulti);
+
+ // set the value of the key "foo" in section "standard"
+ ini.SetValue(_T("standard"), _T("foo"), _T("wibble"));
+ pszVal = ini.GetValue(_T("standard"), _T("foo"), 0, &bHasMulti);
+ _tprintf(_T("\n-- Value of standard::foo is '%s' (hasMulti = %d)\n"),
+ pszVal ? pszVal : _T("(null)"), bHasMulti);
+
+ // get all values of the key "foo" in section "standard"
+ CSimpleIni::TNamesDepend values;
+ if (ini.GetAllValues(_T("standard"), _T("foo"), values)) {
+ _tprintf(_T("\n-- Values of standard::foo are:\n"));
+ CSimpleIni::TNamesDepend::const_iterator i = values.begin();
+ for (; i != values.end(); ++i) {
+ pszVal = i->pItem;
+ _tprintf(_T(" -> '%s'\n"), pszVal);
+ }
+ }
+
+ // get the size of the section [standard]
+ _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"),
+ ini.GetSectionSize(_T("standard")));
+
+ // delete the key "foo" in section "standard"
+ ini.Delete(_T("standard"), _T("foo"));
+ pszVal = ini.GetValue(_T("standard"), _T("foo"), 0);
+ _tprintf(_T("\n-- Value of standard::foo is now '%s'\n"),
+ pszVal ? pszVal : _T("(null)"));
+
+ // get the size of the section [standard]
+ _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"),
+ ini.GetSectionSize(_T("standard")));
+
+ // get the list of all key names for the section "standard"
+ _tprintf(_T("\n-- Dumping keys of section: [standard]\n"));
+ CSimpleIni::TNamesDepend keys;
+ ini.GetAllKeys(_T("standard"), keys);
+
+ // dump all of the key names
+ CSimpleIni::TNamesDepend::const_iterator iKey = keys.begin();
+ for ( ; iKey != keys.end(); ++iKey ) {
+ pItem = iKey->pItem;
+ _tprintf(_T("Key: %s\n"), pItem);
+ }
+
+ // add a decimal value
+ ini.SetLongValue(_T("integer"), _T("dec"), 42, NULL, false);
+ ini.SetLongValue(_T("integer"), _T("hex"), 42, NULL, true);
+
+ // add some bool values
+ ini.SetBoolValue(_T("bool"), _T("t"), true);
+ ini.SetBoolValue(_T("bool"), _T("f"), false);
+
+ // get the values back
+ assert(42 == ini.GetLongValue(_T("integer"), _T("dec")));
+ assert(42 == ini.GetLongValue(_T("integer"), _T("hex")));
+ assert(true == ini.GetBoolValue(_T("bool"), _T("t")));
+ assert(false == ini.GetBoolValue(_T("bool"), _T("f")));
+
+ // delete the section "standard"
+ ini.Delete(_T("standard"), NULL);
+ _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"),
+ ini.GetSectionSize(_T("standard")));
+
+ // iterate through every section in the file
+ _tprintf(_T("\n-- Dumping all sections\n"));
+ CSimpleIni::TNamesDepend sections;
+ ini.GetAllSections(sections);
+ CSimpleIni::TNamesDepend::const_iterator iSection = sections.begin();
+ for ( ; iSection != sections.end(); ++iSection ) {
+ pszSection = iSection->pItem;
+
+ // print the section name
+ printf("\n");
+ if (*pszSection) {
+ _tprintf(_T("[%s]\n"), pszSection);
+ }
+
+ // if there are keys and values...
+ const CSimpleIni::TKeyVal * pSectionData = ini.GetSection(pszSection);
+ if (pSectionData) {
+ // iterate over all keys and dump the key name and value
+ CSimpleIni::TKeyVal::const_iterator iKeyVal = pSectionData->begin();
+ for ( ;iKeyVal != pSectionData->end(); ++iKeyVal) {
+ pItem = iKeyVal->first.pItem;
+ pszVal = iKeyVal->second;
+ _tprintf(_T("%s=%s\n"), pItem, pszVal);
+ }
+ }
+ }
+}
+
+#if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE)
+static bool
+TestStreams(
+ const TCHAR * a_pszFile,
+ bool a_bIsUtf8,
+ bool a_bUseMultiKey,
+ bool a_bUseMultiLine
+ )
+{
+ // load the file
+ CSimpleIni ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine);
+ _tprintf(_T("Loading file: %s\n"), a_pszFile);
+ std::ifstream instream;
+ instream.open(a_pszFile, std::ifstream::in | std::ifstream::binary);
+ SI_Error rc = ini.LoadData(instream);
+ instream.close();
+ if (rc < 0) {
+ printf("Failed to open file.\n");
+ return false;
+ }
+
+ Test(ini);
+
+ // save the file (simple)
+ _tprintf(_T("\n-- Saving file to: testsi-out-streams.ini\n"));
+ std::ofstream outstream;
+ outstream.open("testsi-out-streams.ini", std::ofstream::out | std::ofstream::binary);
+ ini.Save(outstream);
+ outstream.close();
+
+ return true;
+}
+#endif // SI_SUPPORT_IOSTREAMS
+
+static bool
+TestFile(
+ const TCHAR * a_pszFile,
+ bool a_bIsUtf8,
+ bool a_bUseMultiKey,
+ bool a_bUseMultiLine
+ )
+{
+ // load the file
+ CSimpleIni ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine);
+ _tprintf(_T("Loading file: %s\n"), a_pszFile);
+ SI_Error rc = ini.LoadFile(a_pszFile);
+ if (rc < 0) {
+ printf("Failed to open file.\n");
+ return false;
+ }
+
+ // run the tests
+ Test(ini);
+
+ // save the file (simple)
+ _tprintf(_T("\n-- Saving file to: testsi-out.ini\n"));
+ ini.SaveFile("testsi-out.ini");
+
+ // save the file (with comments)
+ // Note: to save the file and add a comment to the beginning, use
+ // code such as the following.
+ _tprintf(_T("\n-- Saving file to: testsi-out-comment.ini\n"));
+ FILE * fp = NULL;
+#if __STDC_WANT_SECURE_LIB__
+ fopen_s(&fp, "testsi-out-comment.ini", "wb");
+#else
+ fp = fopen("testsi-out-comment.ini", "wb");
+#endif
+ if (fp) {
+ CSimpleIni::FileWriter writer(fp);
+ if (a_bIsUtf8) {
+ writer.Write(SI_UTF8_SIGNATURE);
+ }
+
+ // add a string to the file in the correct text format
+ CSimpleIni::Converter convert = ini.GetConverter();
+ convert.ConvertToStore(_T("; output from testsi.cpp test program")
+ SI_NEWLINE SI_NEWLINE);
+ writer.Write(convert.Data());
+
+ ini.Save(writer, false);
+ fclose(fp);
+ }
+
+ return true;
+}
+
+static bool
+ParseCommandLine(
+ int argc,
+ TCHAR * argv[],
+ const TCHAR * & a_pszFile,
+ bool & a_bIsUtf8,
+ bool & a_bUseMultiKey,
+ bool & a_bUseMultiLine
+ )
+{
+ a_pszFile = 0;
+ a_bIsUtf8 = false;
+ a_bUseMultiKey = false;
+ a_bUseMultiLine = false;
+ for (--argc; argc > 0; --argc) {
+ if (argv[argc][0] == '-') {
+ switch (argv[argc][1]) {
+ case TCHAR('u'):
+ a_bIsUtf8 = true;
+ break;
+ case TCHAR('m'):
+ a_bUseMultiKey = true;
+ break;
+ case TCHAR('l'):
+ a_bUseMultiLine = true;
+ break;
+ }
+ }
+ else {
+ a_pszFile = argv[argc];
+ }
+ }
+ if (!a_pszFile) {
+ _tprintf(
+ _T("Usage: testsi [-u] [-m] [-l] iniFile\n")
+ _T(" -u Load file as UTF-8 (Default is to use system locale)\n")
+ _T(" -m Enable multiple keys\n")
+ _T(" -l Enable multiple line values\n")
+ );
+ return false;
+ }
+
+ return true;
+}
+
+extern bool TestStreams();
+
+int
+_tmain(
+ int argc,
+ TCHAR * argv[]
+ )
+{
+ setlocale(LC_ALL, "");
+
+ // start of automated testing...
+ TestStreams();
+
+ // parse the command line
+ const TCHAR * pszFile;
+ bool bIsUtf8, bUseMultiKey, bUseMultiLine;
+ if (!ParseCommandLine(argc, argv, pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) {
+ return 1;
+ }
+
+ // run the test
+ if (!TestFile(pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) {
+ return 1;
+ }
+#if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE)
+ if (!TestStreams(pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) {
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+