SimpleIni
Loading...
Searching...
No Matches
SimpleIni.h
1
223#ifndef INCLUDED_SimpleIni_h
224#define INCLUDED_SimpleIni_h
225
226#if defined(_MSC_VER) && (_MSC_VER >= 1020)
227#pragma once
228#endif
229
230// Disable these warnings in MSVC:
231// 4127 "conditional expression is constant" as the conversion classes trigger
232// it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
233// be optimized away in a release build.
234// 4503 'insert' : decorated name length exceeded, name was truncated
235// 4702 "unreachable code" as the MS STL header causes it in release mode.
236// Again, the code causing the warning will be cleaned up by the compiler.
237// 4786 "identifier truncated to 256 characters" as this is thrown hundreds
238// of times VC6 as soon as STL is used.
239#ifdef _MSC_VER
240#pragma warning(push)
241#pragma warning(disable : 4127 4503 4702 4786)
242#endif
243
244#include <algorithm>
245#include <cstdlib>
246#include <cstring>
247#include <limits>
248#include <list>
249#include <map>
250#include <stdio.h>
251#include <string>
252
253#ifdef SI_SUPPORT_IOSTREAMS
254#include <iostream>
255#endif // SI_SUPPORT_IOSTREAMS
256
257#ifdef _DEBUG
258#ifndef assert
259#include <cassert>
260#endif
261#define SI_ASSERT(x) assert(x)
262#else
263#define SI_ASSERT(x)
264#endif
265
266using SI_Error = int;
267
268constexpr int SI_OK = 0;
269constexpr int SI_UPDATED = 1;
270constexpr int SI_INSERTED = 2;
271
272// note: test for any error with (retval < 0)
273constexpr int SI_FAIL = -1;
274constexpr int SI_NOMEM = -2;
275constexpr int SI_FILE = -3;
276
279constexpr size_t SI_MAX_FILE_SIZE = 1024ULL * 1024ULL * 1024ULL;
280
281#define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
282
283#ifdef _WIN32
284#define SI_NEWLINE_A "\r\n"
285#define SI_NEWLINE_W L"\r\n"
286#else // !_WIN32
287#define SI_NEWLINE_A "\n"
288#define SI_NEWLINE_W L"\n"
289#endif // _WIN32
290
291#if defined(SI_CONVERT_ICU)
292#include <unicode/ustring.h>
293#endif
294
295#if defined(_WIN32)
296#define SI_HAS_WIDE_FILE
297#define SI_WCHAR_T wchar_t
298#elif defined(SI_CONVERT_ICU)
299#define SI_HAS_WIDE_FILE
300#define SI_WCHAR_T UChar
301#endif
302
303// ---------------------------------------------------------------------------
304// MAIN TEMPLATE CLASS
305// ---------------------------------------------------------------------------
306
326template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
328public:
329 typedef SI_CHAR SI_CHAR_T;
330
332 struct Entry {
333 const SI_CHAR *pItem;
334 const SI_CHAR *pComment;
335 int nOrder;
336
337 Entry(const SI_CHAR *a_pszItem = NULL, int a_nOrder = 0)
338 : pItem(a_pszItem), pComment(NULL), nOrder(a_nOrder) {}
339 Entry(const SI_CHAR *a_pszItem, const SI_CHAR *a_pszComment, int a_nOrder)
340 : pItem(a_pszItem), pComment(a_pszComment), nOrder(a_nOrder) {}
341 Entry(const Entry &rhs) { operator=(rhs); }
342 Entry &operator=(const Entry &rhs) {
343 pItem = rhs.pItem;
344 pComment = rhs.pComment;
345 nOrder = rhs.nOrder;
346 return *this;
347 }
348
349#if defined(_MSC_VER) && _MSC_VER <= 1200
351 bool operator<(const Entry &rhs) const { return LoadOrder()(*this, rhs); }
352 bool operator>(const Entry &rhs) const { return LoadOrder()(rhs, *this); }
353#endif
354
356 struct KeyOrder {
357 bool operator()(const Entry &lhs, const Entry &rhs) const {
358 const static SI_STRLESS isLess = SI_STRLESS();
359 return isLess(lhs.pItem, rhs.pItem);
360 }
361 };
362
364 struct LoadOrder {
365 bool operator()(const Entry &lhs, const Entry &rhs) const {
366 if (lhs.nOrder != rhs.nOrder) {
367 return lhs.nOrder < rhs.nOrder;
368 }
369 return KeyOrder()(lhs, rhs);
370 }
371 };
372 };
373
375 typedef std::multimap<Entry, const SI_CHAR *, typename Entry::KeyOrder>
377
379 typedef std::map<Entry, TKeyVal, typename Entry::KeyOrder> TSection;
380
384 typedef std::list<Entry> TNamesDepend;
385
390 public:
391 OutputWriter() {}
392 virtual ~OutputWriter() {}
393 virtual void Write(const char *a_pBuf) = 0;
394
395 private:
396 OutputWriter(const OutputWriter &); // disable
397 OutputWriter &operator=(const OutputWriter &); // disable
398 };
399
401 class FileWriter : public OutputWriter {
402 FILE *m_file;
403
404 public:
405 FileWriter(FILE *a_file) : m_file(a_file) {}
406 void Write(const char *a_pBuf) { fputs(a_pBuf, m_file); }
407
408 private:
409 FileWriter(const FileWriter &); // disable
410 FileWriter &operator=(const FileWriter &); // disable
411 };
412
414 class StringWriter : public OutputWriter {
415 std::string &m_string;
416
417 public:
418 StringWriter(std::string &a_string) : m_string(a_string) {}
419 void Write(const char *a_pBuf) { m_string.append(a_pBuf); }
420
421 private:
422 StringWriter(const StringWriter &); // disable
423 StringWriter &operator=(const StringWriter &); // disable
424 };
425
426#ifdef SI_SUPPORT_IOSTREAMS
428 class StreamWriter : public OutputWriter {
429 std::ostream &m_ostream;
430
431 public:
432 StreamWriter(std::ostream &a_ostream) : m_ostream(a_ostream) {}
433 void Write(const char *a_pBuf) { m_ostream << a_pBuf; }
434
435 private:
436 StreamWriter(const StreamWriter &); // disable
437 StreamWriter &operator=(const StreamWriter &); // disable
438 };
439#endif // SI_SUPPORT_IOSTREAMS
440
444 class Converter : private SI_CONVERTER {
445 public:
446 Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) {
447 m_scratch.resize(1024);
448 }
449 Converter(const Converter &rhs) { operator=(rhs); }
450 Converter &operator=(const Converter &rhs) {
451 m_scratch = rhs.m_scratch;
452 return *this;
453 }
454 bool ConvertToStore(const SI_CHAR *a_pszString) {
455 size_t uLen = SI_CONVERTER::SizeToStore(a_pszString);
456 if (uLen == (size_t)(-1)) {
457 return false;
458 }
459 while (uLen > m_scratch.size()) {
460 m_scratch.resize(m_scratch.size() * 2);
461 }
462 return SI_CONVERTER::ConvertToStore(
463 a_pszString, const_cast<char *>(m_scratch.data()), m_scratch.size());
464 }
465 const char *Data() { return m_scratch.data(); }
466
467 private:
468 std::string m_scratch;
469 };
470
471public:
472 /*-----------------------------------------------------------------------*/
473
480 CSimpleIniTempl(bool a_bIsUtf8 = false, bool a_bMultiKey = false,
481 bool a_bMultiLine = false);
482
485
487 void Reset();
488
490 bool IsEmpty() const { return m_data.empty(); }
491
492 /*-----------------------------------------------------------------------*/
509 void SetUnicode(bool a_bIsUtf8 = true) {
510 if (!m_pData)
511 m_bStoreIsUtf8 = a_bIsUtf8;
512 }
513
515 bool IsUnicode() const { return m_bStoreIsUtf8; }
516
535 void SetMultiKey(bool a_bAllowMultiKey = true) {
536 m_bAllowMultiKey = a_bAllowMultiKey;
537 }
538
540 bool IsMultiKey() const { return m_bAllowMultiKey; }
541
549 void SetMultiLine(bool a_bAllowMultiLine = true) {
550 m_bAllowMultiLine = a_bAllowMultiLine;
551 }
552
554 bool IsMultiLine() const { return m_bAllowMultiLine; }
555
562 void SetSpaces(bool a_bSpaces = true) { m_bSpaces = a_bSpaces; }
563
565 bool UsingSpaces() const { return m_bSpaces; }
566
571 void SetQuotes(bool a_bParseQuotes = true) {
572 m_bParseQuotes = a_bParseQuotes;
573 }
574
576 bool UsingQuotes() const { return m_bParseQuotes; }
577
586 void SetAllowKeyOnly(bool a_bAllowKeyOnly = true) {
587 m_bAllowKeyOnly = a_bAllowKeyOnly;
588 }
589
591 bool GetAllowKeyOnly() const { return m_bAllowKeyOnly; }
592
593 /*-----------------------------------------------------------------------*/
605 SI_Error LoadFile(const char *a_pszFile);
606
607#ifdef SI_HAS_WIDE_FILE
614 SI_Error LoadFile(const SI_WCHAR_T *a_pwszFile);
615#endif // SI_HAS_WIDE_FILE
616
624 SI_Error LoadFile(FILE *a_fpFile);
625
626#ifdef SI_SUPPORT_IOSTREAMS
637 SI_Error LoadData(std::istream &a_istream);
638#endif // SI_SUPPORT_IOSTREAMS
639
646 SI_Error LoadData(const std::string &a_strData) {
647 return LoadData(a_strData.c_str(), a_strData.size());
648 }
649
657 SI_Error LoadData(const char *a_pData, size_t a_uDataLen);
658
659 /*-----------------------------------------------------------------------*/
675 SI_Error SaveFile(const char *a_pszFile, bool a_bAddSignature = true) const;
676
677#ifdef SI_HAS_WIDE_FILE
688 SI_Error SaveFile(const SI_WCHAR_T *a_pwszFile,
689 bool a_bAddSignature = true) const;
690#endif // _WIN32
691
704 SI_Error SaveFile(FILE *a_pFile, bool a_bAddSignature = false) const;
705
737 SI_Error Save(OutputWriter &a_oOutput, bool a_bAddSignature = false) const;
738
739#ifdef SI_SUPPORT_IOSTREAMS
751 SI_Error Save(std::ostream &a_ostream, bool a_bAddSignature = false) const {
752 StreamWriter writer(a_ostream);
753 return Save(writer, a_bAddSignature);
754 }
755#endif // SI_SUPPORT_IOSTREAMS
756
768 SI_Error Save(std::string &a_sBuffer, bool a_bAddSignature = false) const {
769 StringWriter writer(a_sBuffer);
770 return Save(writer, a_bAddSignature);
771 }
772
773 /*-----------------------------------------------------------------------*/
791 void GetAllSections(TNamesDepend &a_names) const;
792
810 bool GetAllKeys(const SI_CHAR *a_pSection, TNamesDepend &a_names) const;
811
828 bool GetAllValues(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
829 TNamesDepend &a_values) const;
830
840 int GetSectionSize(const SI_CHAR *a_pSection) const;
841
855 const TKeyVal *GetSection(const SI_CHAR *a_pSection) const;
856
858 inline bool SectionExists(const SI_CHAR *a_pSection) const {
859 return GetSection(a_pSection) != NULL;
860 }
861
863 inline bool KeyExists(const SI_CHAR *a_pSection,
864 const SI_CHAR *a_pKey) const {
865 return GetValue(a_pSection, a_pKey) != NULL;
866 }
867
885 const SI_CHAR *GetValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
886 const SI_CHAR *a_pDefault = NULL,
887 bool *a_pHasMultiple = NULL) const;
888
902 long GetLongValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
903 long a_nDefault = 0, bool *a_pHasMultiple = NULL) const;
904
918 double GetDoubleValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
919 double a_nDefault = 0,
920 bool *a_pHasMultiple = NULL) const;
921
940 bool GetBoolValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
941 bool a_bDefault = false, bool *a_pHasMultiple = NULL) const;
942
972 SI_Error SetValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
973 const SI_CHAR *a_pValue, const SI_CHAR *a_pComment = NULL,
974 bool a_bForceReplace = false) {
975 return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace,
976 true);
977 }
978
1002 SI_Error SetLongValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
1003 long a_nValue, const SI_CHAR *a_pComment = NULL,
1004 bool a_bUseHex = false, bool a_bForceReplace = false);
1005
1026 SI_Error SetDoubleValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
1027 double a_nValue, const SI_CHAR *a_pComment = NULL,
1028 bool a_bForceReplace = false);
1029
1050 SI_Error SetBoolValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
1051 bool a_bValue, const SI_CHAR *a_pComment = NULL,
1052 bool a_bForceReplace = false);
1053
1072 bool Delete(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
1073 bool a_bRemoveEmpty = false);
1074
1095 bool DeleteValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
1096 const SI_CHAR *a_pValue, bool a_bRemoveEmpty = false);
1097
1098 /*-----------------------------------------------------------------------*/
1107 Converter GetConverter() const { return Converter(m_bStoreIsUtf8); }
1108
1109 /*-----------------------------------------------------------------------*/
1112private:
1113 // copying is not permitted
1114 CSimpleIniTempl(const CSimpleIniTempl &); // disabled
1115 CSimpleIniTempl &operator=(const CSimpleIniTempl &); // disabled
1116
1119 SI_Error FindFileComment(SI_CHAR *&a_pData, bool a_bCopyStrings);
1120
1125 bool FindEntry(SI_CHAR *&a_pData, const SI_CHAR *&a_pSection,
1126 const SI_CHAR *&a_pKey, const SI_CHAR *&a_pVal,
1127 const SI_CHAR *&a_pComment) const;
1128
1151 SI_Error AddEntry(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
1152 const SI_CHAR *a_pValue, const SI_CHAR *a_pComment,
1153 bool a_bForceReplace, bool a_bCopyStrings);
1154
1156 inline bool IsSpace(SI_CHAR ch) const {
1157 return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
1158 }
1159
1161 inline bool IsComment(SI_CHAR ch) const { return (ch == ';' || ch == '#'); }
1162
1164 inline void SkipNewLine(SI_CHAR *&a_pData) const {
1165 a_pData += (*a_pData == '\r' && *(a_pData + 1) == '\n') ? 2 : 1;
1166 }
1167
1169 SI_Error CopyString(const SI_CHAR *&a_pString);
1170
1172 void UndoIncrementalLoadData(const TNamesDepend &a_oAddedSections,
1173 const TNamesDepend &a_oAddedKeys);
1174
1176 void DeleteString(const SI_CHAR *a_pString);
1177
1179 bool IsLess(const SI_CHAR *a_pLeft, const SI_CHAR *a_pRight) const {
1180 const static SI_STRLESS isLess = SI_STRLESS();
1181 return isLess(a_pLeft, a_pRight);
1182 }
1183
1184 bool IsMultiLineTag(const SI_CHAR *a_pData) const;
1185 bool IsMultiLineData(const SI_CHAR *a_pData) const;
1186 bool IsSingleLineQuotedValue(const SI_CHAR *a_pData) const;
1187 bool LoadMultiLineText(SI_CHAR *&a_pData, const SI_CHAR *&a_pVal,
1188 const SI_CHAR *a_pTagName,
1189 bool a_bAllowBlankLinesInComment = false) const;
1190 bool IsNewLineChar(SI_CHAR a_c) const;
1191
1192 bool OutputMultiLineText(OutputWriter &a_oOutput, Converter &a_oConverter,
1193 const SI_CHAR *a_pText) const;
1194
1195private:
1201 SI_CHAR *m_pData;
1202
1207 size_t m_uDataLen;
1208
1210 const SI_CHAR *m_pFileComment;
1211
1213 const SI_CHAR m_cEmptyString;
1214
1216 TSection m_data;
1217
1222 TNamesDepend m_strings;
1223
1225 bool m_bStoreIsUtf8;
1226
1228 bool m_bAllowMultiKey;
1229
1231 bool m_bAllowMultiLine;
1232
1234 bool m_bSpaces;
1235
1237 bool m_bParseQuotes;
1238
1240 bool m_bAllowKeyOnly;
1241
1245 int m_nOrder;
1246};
1247
1248// ---------------------------------------------------------------------------
1249// IMPLEMENTATION
1250// ---------------------------------------------------------------------------
1251
1252template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1254 bool a_bIsUtf8, bool a_bAllowMultiKey, bool a_bAllowMultiLine)
1255 : m_pData(0), m_uDataLen(0), m_pFileComment(NULL), m_cEmptyString(0),
1256 m_bStoreIsUtf8(a_bIsUtf8), m_bAllowMultiKey(a_bAllowMultiKey),
1257 m_bAllowMultiLine(a_bAllowMultiLine), m_bSpaces(true),
1258 m_bParseQuotes(false), m_bAllowKeyOnly(false), m_nOrder(0) {}
1259
1260template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1264
1265template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1267 // remove all data
1268 delete[] m_pData;
1269 m_pData = NULL;
1270 m_uDataLen = 0;
1271 m_pFileComment = NULL;
1272 m_nOrder = 0;
1273 if (!m_data.empty()) {
1274 m_data.erase(m_data.begin(), m_data.end());
1275 }
1276
1277 // remove all strings
1278 if (!m_strings.empty()) {
1279 typename TNamesDepend::iterator i = m_strings.begin();
1280 for (; i != m_strings.end(); ++i) {
1281 delete[] const_cast<SI_CHAR *>(i->pItem);
1282 }
1283 m_strings.erase(m_strings.begin(), m_strings.end());
1284 }
1285}
1286
1287namespace SI_Internal {
1288#ifdef SI_CONVERT_ICU
1290inline SI_Error UCharPathToUtf8(const UChar *a_pPath, char *&a_pUtf8Path) {
1291 a_pUtf8Path = NULL;
1292 UErrorCode status = U_ZERO_ERROR;
1293 int32_t uLen = 0;
1294 u_strToUTF8(NULL, 0, &uLen, a_pPath, -1, &status);
1295 if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
1296 return SI_FILE;
1297 }
1298 if (uLen < 0) {
1299 return SI_FILE;
1300 }
1301 char *pBuf = new (std::nothrow) char[static_cast<size_t>(uLen) + 1];
1302 if (!pBuf) {
1303 return SI_NOMEM;
1304 }
1305 status = U_ZERO_ERROR;
1306 u_strToUTF8(pBuf, uLen + 1, NULL, a_pPath, -1, &status);
1307 if (U_FAILURE(status)) {
1308 delete[] pBuf;
1309 return SI_FILE;
1310 }
1311 a_pUtf8Path = pBuf;
1312 return SI_OK;
1313}
1314#endif // SI_CONVERT_ICU
1315
1317inline bool GetFileSize(FILE *a_fpFile, size_t &a_uSize) {
1318 a_uSize = 0;
1319
1320#if defined(_WIN32) && !defined(_WIN32_WCE)
1321 if (_fseeki64(a_fpFile, 0, SEEK_END) != 0) {
1322 return false;
1323 }
1324 const __int64 nSize = _ftelli64(a_fpFile);
1325 if (nSize < 0) {
1326 return false;
1327 }
1328 if (static_cast<unsigned long long>(nSize) >
1329 static_cast<unsigned long long>(SI_MAX_FILE_SIZE)) {
1330 return false;
1331 }
1332 if (static_cast<unsigned long long>(nSize) >
1333 static_cast<unsigned long long>(std::numeric_limits<size_t>::max())) {
1334 return false;
1335 }
1336 a_uSize = static_cast<size_t>(nSize);
1337 if (_fseeki64(a_fpFile, 0, SEEK_SET) != 0) {
1338 return false;
1339 }
1340 return true;
1341#elif defined(_POSIX_VERSION) || defined(__unix__) || defined(__APPLE__)
1342 if (fseeko(a_fpFile, 0, SEEK_END) != 0) {
1343 return false;
1344 }
1345 const off_t nSize = ftello(a_fpFile);
1346 if (nSize < 0) {
1347 return false;
1348 }
1349 if (static_cast<unsigned long long>(nSize) >
1350 static_cast<unsigned long long>(SI_MAX_FILE_SIZE)) {
1351 return false;
1352 }
1353 if (static_cast<unsigned long long>(nSize) >
1354 static_cast<unsigned long long>(std::numeric_limits<size_t>::max())) {
1355 return false;
1356 }
1357 a_uSize = static_cast<size_t>(nSize);
1358 if (fseeko(a_fpFile, 0, SEEK_SET) != 0) {
1359 return false;
1360 }
1361 return true;
1362#else
1363 if (fseek(a_fpFile, 0, SEEK_END) != 0) {
1364 return false;
1365 }
1366 const long nSize = ftell(a_fpFile);
1367 if (nSize < 0) {
1368 return false;
1369 }
1370 if (static_cast<unsigned long long>(nSize) >
1371 static_cast<unsigned long long>(SI_MAX_FILE_SIZE)) {
1372 return false;
1373 }
1374 a_uSize = static_cast<size_t>(nSize);
1375 if (fseek(a_fpFile, 0, SEEK_SET) != 0) {
1376 return false;
1377 }
1378 return true;
1379#endif
1380}
1381} // namespace SI_Internal
1382
1383template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1385 const char *a_pszFile) {
1386 FILE *fp = NULL;
1387#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1388 fopen_s(&fp, a_pszFile, "rb");
1389#else // !__STDC_WANT_SECURE_LIB__
1390 fp = fopen(a_pszFile, "rb");
1391#endif // __STDC_WANT_SECURE_LIB__
1392 if (!fp) {
1393 return SI_FILE;
1394 }
1395 SI_Error rc = LoadFile(fp);
1396 fclose(fp);
1397 return rc;
1398}
1399
1400#ifdef SI_HAS_WIDE_FILE
1401template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1403 const SI_WCHAR_T *a_pwszFile) {
1404#ifdef _WIN32
1405 FILE *fp = NULL;
1406#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1407 _wfopen_s(&fp, a_pwszFile, L"rb");
1408#else // !__STDC_WANT_SECURE_LIB__
1409 fp = _wfopen(a_pwszFile, L"rb");
1410#endif // __STDC_WANT_SECURE_LIB__
1411 if (!fp)
1412 return SI_FILE;
1413 SI_Error rc = LoadFile(fp);
1414 fclose(fp);
1415 return rc;
1416#else // !_WIN32 (therefore SI_CONVERT_ICU)
1417 char *szFile = NULL;
1418 SI_Error rcPath = SI_Internal::UCharPathToUtf8(a_pwszFile, szFile);
1419 if (rcPath < 0) {
1420 return rcPath;
1421 }
1422 SI_Error rc = LoadFile(szFile);
1423 delete[] szFile;
1424 return rc;
1425#endif // _WIN32
1426}
1427#endif // SI_HAS_WIDE_FILE
1428
1429template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1430SI_Error
1432 size_t uSize = 0;
1433 if (!SI_Internal::GetFileSize(a_fpFile, uSize)) {
1434 return SI_FILE;
1435 }
1436 if (uSize == 0) {
1437 return SI_OK;
1438 }
1439
1440 // allocate and ensure NULL terminated
1441 char *pData = new (std::nothrow) char[uSize + 1];
1442 if (!pData) {
1443 return SI_NOMEM;
1444 }
1445 pData[uSize] = 0;
1446
1447 // load data into buffer
1448 size_t uRead = fread(pData, sizeof(char), uSize, a_fpFile);
1449 if (uRead != uSize) {
1450 delete[] pData;
1451 return SI_FILE;
1452 }
1453
1454 // convert the raw data to unicode
1455 SI_Error rc = LoadData(pData, uRead);
1456 delete[] pData;
1457 return rc;
1458}
1459
1460template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1462 const char *a_pData, size_t a_uDataLen) {
1463 if (!a_pData) {
1464 return SI_OK;
1465 }
1466
1467 // if the UTF-8 BOM exists, consume it and set mode to unicode, if we have
1468 // already loaded data and try to change mode half-way through then this will
1469 // be ignored and we will assert in debug versions
1470 if (a_uDataLen >= 3 && memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
1471 a_pData += 3;
1472 a_uDataLen -= 3;
1473 SI_ASSERT(m_bStoreIsUtf8 || !m_pData); // we don't expect mixed mode data
1474 SetUnicode();
1475 }
1476
1477 if (a_uDataLen == 0) {
1478 return SI_OK;
1479 }
1480
1481 // determine the length of the converted data
1482 SI_CONVERTER converter(m_bStoreIsUtf8);
1483 size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
1484 if (uLen == (size_t)(-1)) {
1485 return SI_FAIL;
1486 }
1487
1488 // check converted data size is within supported limits (SI_MAX_FILE_SIZE)
1489 if (uLen >= (SI_MAX_FILE_SIZE / sizeof(SI_CHAR))) {
1490 return SI_FILE;
1491 }
1492
1493 // allocate memory for the data, ensure that there is a NULL
1494 // terminator wherever the converted data ends
1495 SI_CHAR *pData = new (std::nothrow) SI_CHAR[uLen + 1];
1496 if (!pData) {
1497 return SI_NOMEM;
1498 }
1499 memset(pData, 0, sizeof(SI_CHAR) * (uLen + 1));
1500
1501 // convert the data
1502 if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
1503 delete[] pData;
1504 return SI_FAIL;
1505 }
1506
1507 // parse it
1508 const static SI_CHAR empty = 0;
1509 SI_CHAR *pWork = pData;
1510 const SI_CHAR *pSection = &empty;
1511 const SI_CHAR *pItem = NULL;
1512 const SI_CHAR *pVal = NULL;
1513 const SI_CHAR *pComment = NULL;
1514
1515 // We copy the strings if we are loading data into this class when we
1516 // already have stored some.
1517 bool bCopyStrings = (m_pData != NULL);
1518
1519 size_t nStringsBefore = 0;
1520 const SI_CHAR *pFileCommentBefore = NULL;
1521 TNamesDepend oAddedSections;
1522 TNamesDepend oAddedKeys;
1523 if (bCopyStrings) {
1524 nStringsBefore = m_strings.size();
1525 pFileCommentBefore = m_pFileComment;
1526 }
1527
1528 // find a file comment if it exists, this is a comment that starts at the
1529 // beginning of the file and continues until the first blank line.
1530 SI_Error rc = FindFileComment(pWork, bCopyStrings);
1531 if (rc < 0) {
1532 delete[] pData;
1533 if (bCopyStrings) {
1534 m_pFileComment = pFileCommentBefore;
1535 while (m_strings.size() > nStringsBefore) {
1536 delete[] const_cast<SI_CHAR *>(m_strings.back().pItem);
1537 m_strings.pop_back();
1538 }
1539 } else {
1540 m_pFileComment = NULL;
1541 }
1542 return rc;
1543 }
1544
1545 // add every entry in the file to the data table
1546 while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
1547 bool bSectionExisted = SectionExists(pSection);
1548 bool bKeyExisted = pItem && bSectionExisted && KeyExists(pSection, pItem);
1549
1550 rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings);
1551 if (rc < 0) {
1552 if (bCopyStrings) {
1553 m_pFileComment = pFileCommentBefore;
1554 UndoIncrementalLoadData(oAddedSections, oAddedKeys);
1555 while (m_strings.size() > nStringsBefore) {
1556 delete[] const_cast<SI_CHAR *>(m_strings.back().pItem);
1557 m_strings.pop_back();
1558 }
1559 } else {
1560 m_data.clear();
1561 m_pFileComment = NULL;
1562 m_nOrder = 0;
1563 }
1564 delete[] pData;
1565 return rc;
1566 }
1567
1568 if (bCopyStrings) {
1569 if (!bSectionExisted && *pSection) {
1570 oAddedSections.push_back(Entry(pSection, NULL, 0));
1571 }
1572 if (pItem && !bKeyExisted) {
1573 oAddedKeys.push_back(Entry(pItem, pSection, 0));
1574 }
1575 }
1576 }
1577
1578 // store these strings if we didn't copy them
1579 if (bCopyStrings) {
1580 delete[] pData;
1581 } else {
1582 m_pData = pData;
1583 m_uDataLen = uLen + 1;
1584 }
1585
1586 return SI_OK;
1587}
1588
1589#ifdef SI_SUPPORT_IOSTREAMS
1590template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1592 std::istream &a_istream) {
1593 std::string strData;
1594 char buf[4096];
1595 while (a_istream.read(buf, sizeof(buf)) || a_istream.gcount() > 0) {
1596 const std::streamsize nRead = a_istream.gcount();
1597 for (std::streamsize i = 0; i < nRead; ++i) {
1598 if (buf[i] == '\0') {
1599 return SI_FAIL;
1600 }
1601 }
1602 if (strData.size() + static_cast<size_t>(nRead) > SI_MAX_FILE_SIZE) {
1603 return SI_FILE;
1604 }
1605 strData.append(buf, static_cast<size_t>(nRead));
1606 }
1607 return LoadData(strData.data(), strData.size());
1608}
1609#endif // SI_SUPPORT_IOSTREAMS
1610
1611template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1613 SI_CHAR *&a_pData, bool a_bCopyStrings) {
1614 // there can only be a single file comment
1615 if (m_pFileComment) {
1616 return SI_OK;
1617 }
1618
1619 // Load the file comment as multi-line text, this will modify all of
1620 // the newline characters to be single \n chars
1621 if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
1622 return SI_OK;
1623 }
1624
1625 // copy the string if necessary
1626 if (a_bCopyStrings) {
1627 SI_Error rc = CopyString(m_pFileComment);
1628 if (rc < 0)
1629 return rc;
1630 }
1631
1632 return SI_OK;
1633}
1634
1635template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1637 SI_CHAR *&a_pData, const SI_CHAR *&a_pSection, const SI_CHAR *&a_pKey,
1638 const SI_CHAR *&a_pVal, const SI_CHAR *&a_pComment) const {
1639 a_pComment = NULL;
1640
1641 bool bHaveValue = false;
1642 SI_CHAR *pTrail = NULL;
1643 while (*a_pData) {
1644 // skip spaces and empty lines
1645 while (*a_pData && IsSpace(*a_pData)) {
1646 ++a_pData;
1647 }
1648 if (!*a_pData) {
1649 break;
1650 }
1651
1652 // skip processing of comment lines but keep a pointer to
1653 // the start of the comment.
1654 if (IsComment(*a_pData)) {
1655 LoadMultiLineText(a_pData, a_pComment, NULL, true);
1656 continue;
1657 }
1658
1659 // process section names
1660 if (*a_pData == '[') {
1661 // skip leading spaces
1662 ++a_pData;
1663 while (*a_pData && IsSpace(*a_pData)) {
1664 ++a_pData;
1665 }
1666
1667 // find the end of the section name (it may contain spaces)
1668 // and convert it to lowercase as necessary
1669 a_pSection = a_pData;
1670 while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
1671 ++a_pData;
1672 }
1673
1674 // if it's an invalid line, just skip it
1675 if (*a_pData != ']') {
1676 continue;
1677 }
1678
1679 // remove trailing spaces from the section
1680 pTrail = a_pData - 1;
1681 while (pTrail >= a_pSection && IsSpace(*pTrail)) {
1682 --pTrail;
1683 }
1684 ++pTrail;
1685 *pTrail = 0;
1686
1687 // skip to the end of the line
1688 ++a_pData; // safe as checked that it == ']' above
1689 while (*a_pData && !IsNewLineChar(*a_pData)) {
1690 ++a_pData;
1691 }
1692
1693 a_pKey = NULL;
1694 a_pVal = NULL;
1695 return true;
1696 }
1697
1698 // find the end of the key name (it may contain spaces)
1699 a_pKey = a_pData;
1700 while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
1701 ++a_pData;
1702 }
1703 // *a_pData is null, equals, or newline
1704
1705 // if no value and we don't allow no value, then invalid
1706 bHaveValue = (*a_pData == '=');
1707 if (!bHaveValue && !m_bAllowKeyOnly) {
1708 continue;
1709 }
1710
1711 // empty keys are invalid
1712 if (bHaveValue && a_pKey == a_pData) {
1713 while (*a_pData && !IsNewLineChar(*a_pData)) {
1714 ++a_pData;
1715 }
1716 continue;
1717 }
1718
1719 // remove trailing spaces from the key
1720 pTrail = a_pData - 1;
1721 while (pTrail >= a_pKey && IsSpace(*pTrail)) {
1722 --pTrail;
1723 }
1724 ++pTrail;
1725
1726 if (bHaveValue) {
1727 // process the value
1728 *pTrail = 0;
1729
1730 // skip leading whitespace on the value
1731 ++a_pData; // safe as checked that it == '=' above
1732 while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
1733 ++a_pData;
1734 }
1735
1736 // find the end of the value which is the end of this line
1737 a_pVal = a_pData;
1738 while (*a_pData && !IsNewLineChar(*a_pData)) {
1739 ++a_pData;
1740 }
1741
1742 // remove trailing spaces from the value
1743 pTrail = a_pData - 1;
1744 if (*a_pData) { // prepare for the next round
1745 SkipNewLine(a_pData);
1746 }
1747 while (pTrail >= a_pVal && IsSpace(*pTrail)) {
1748 --pTrail;
1749 }
1750 ++pTrail;
1751 *pTrail = 0;
1752
1753 // check for multi-line entries
1754 if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
1755 // skip the "<<<" to get the tag that will end the multiline
1756 const SI_CHAR *pTagName = a_pVal + 3;
1757 return LoadMultiLineText(a_pData, a_pVal, pTagName);
1758 }
1759
1760 // check for quoted values, we are not supporting escapes in quoted values (yet)
1761 if (m_bParseQuotes) {
1762 --pTrail;
1763 if (pTrail > a_pVal && *a_pVal == '"' && *pTrail == '"') {
1764 ++a_pVal;
1765 *pTrail = 0;
1766 }
1767 }
1768 } else {
1769 // no value to process, just prepare for the next
1770 if (*a_pData) {
1771 SkipNewLine(a_pData);
1772 }
1773 *pTrail = 0;
1774 }
1775
1776 // return the standard entry
1777 return true;
1778 }
1779
1780 return false;
1781}
1782
1783template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1785 const SI_CHAR *a_pVal) const {
1786 // check for the "<<<" prefix for a multi-line entry
1787 if (*a_pVal++ != '<')
1788 return false;
1789 if (*a_pVal++ != '<')
1790 return false;
1791 if (*a_pVal++ != '<')
1792 return false;
1793 return true;
1794}
1795
1796template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1798 const SI_CHAR *a_pData) const {
1799 // data is multi-line if it has any of the following features:
1800 // * whitespace prefix
1801 // * embedded newlines
1802 // * whitespace suffix
1803
1804 // empty string
1805 if (!*a_pData) {
1806 return false;
1807 }
1808
1809 // check for prefix
1810 if (IsSpace(*a_pData)) {
1811 return true;
1812 }
1813
1814 // embedded newlines
1815 const SI_CHAR *pStart = a_pData;
1816 while (*a_pData) {
1817 if (IsNewLineChar(*a_pData)) {
1818 return true;
1819 }
1820 ++a_pData;
1821 }
1822
1823 // check for suffix (ensure we don't go before start of string)
1824 if (a_pData > pStart && IsSpace(*(a_pData - 1))) {
1825 return true;
1826 }
1827
1828 return false;
1829}
1830
1831template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1833 IsSingleLineQuotedValue(const SI_CHAR *a_pData) const {
1834 // data needs quoting if it starts or ends with whitespace
1835 // and doesn't have embedded newlines
1836
1837 // empty string
1838 if (!*a_pData) {
1839 return false;
1840 }
1841
1842 // check for prefix
1843 if (IsSpace(*a_pData)) {
1844 return true;
1845 }
1846
1847 // embedded newlines
1848 const SI_CHAR *pStart = a_pData;
1849 while (*a_pData) {
1850 if (IsNewLineChar(*a_pData)) {
1851 return false;
1852 }
1853 ++a_pData;
1854 }
1855
1856 // check for suffix (ensure we don't go before start of string)
1857 if (a_pData > pStart && IsSpace(*(a_pData - 1))) {
1858 return true;
1859 }
1860
1861 return false;
1862}
1863
1864template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1866 SI_CHAR a_c) const {
1867 return (a_c == '\n' || a_c == '\r');
1868}
1869
1870template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1872 SI_CHAR *&a_pData, const SI_CHAR *&a_pVal, const SI_CHAR *a_pTagName,
1873 bool a_bAllowBlankLinesInComment) const {
1874 // we modify this data to strip all newlines down to a single '\n'
1875 // character. This means that on Windows we need to strip out some
1876 // characters which will make the data shorter.
1877 // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
1878 // LINE1-LINE1\nLINE2-LINE2\0
1879 // The pDataLine entry is the pointer to the location in memory that
1880 // the current line needs to start to run following the existing one.
1881 // This may be the same as pCurrLine in which case no move is needed.
1882 SI_CHAR *pDataLine = a_pData;
1883 SI_CHAR *pCurrLine;
1884
1885 // value starts at the current line
1886 a_pVal = a_pData;
1887
1888 // find the end tag. This tag must start in column 1 and be
1889 // followed by a newline. We ignore any whitespace after the end
1890 // tag but not whitespace before it.
1891 SI_CHAR cEndOfLineChar = *a_pData;
1892 for (;;) {
1893 // if we are loading comments then we need a comment character as
1894 // the first character on every line
1895 if (!a_pTagName && !IsComment(*a_pData)) {
1896 // if we aren't allowing blank lines then we're done
1897 if (!a_bAllowBlankLinesInComment) {
1898 break;
1899 }
1900
1901 // if we are allowing blank lines then we only include them
1902 // in this comment if another comment follows, so read ahead
1903 // to find out.
1904 SI_CHAR *pCurr = a_pData;
1905 int nNewLines = 0;
1906 while (IsSpace(*pCurr)) {
1907 if (IsNewLineChar(*pCurr)) {
1908 ++nNewLines;
1909 SkipNewLine(pCurr);
1910 } else {
1911 ++pCurr;
1912 }
1913 }
1914
1915 // we have a comment, add the blank lines to the output
1916 // and continue processing from here
1917 if (IsComment(*pCurr)) {
1918 for (; nNewLines > 0; --nNewLines)
1919 *pDataLine++ = '\n';
1920 a_pData = pCurr;
1921 continue;
1922 }
1923
1924 // the comment ends here
1925 break;
1926 }
1927
1928 // find the end of this line
1929 pCurrLine = a_pData;
1930 while (*a_pData && !IsNewLineChar(*a_pData))
1931 ++a_pData;
1932
1933 // move this line down to the location that it should be if necessary
1934 if (pDataLine < pCurrLine) {
1935 size_t nLen = (size_t)(a_pData - pCurrLine);
1936 memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
1937 pDataLine[nLen] = '\0';
1938 }
1939
1940 // end the line with a NULL
1941 cEndOfLineChar = *a_pData;
1942 *a_pData = 0;
1943
1944 // if are looking for a tag then do the check now. This is done before
1945 // checking for end of the data, so that if we have the tag at the end
1946 // of the data then the tag is removed correctly.
1947 if (a_pTagName) {
1948 // strip whitespace from the end of this tag
1949 SI_CHAR *pc = a_pData - 1;
1950 while (pc > pDataLine && IsSpace(*pc))
1951 --pc;
1952 SI_CHAR ch = *++pc;
1953 *pc = 0;
1954
1955 if (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)) {
1956 break;
1957 }
1958
1959 *pc = ch;
1960 }
1961
1962 // if we are at the end of the data then we just automatically end
1963 // this entry and return the current data.
1964 if (!cEndOfLineChar) {
1965 return true;
1966 }
1967
1968 // otherwise we need to process this newline to ensure that it consists
1969 // of just a single \n character.
1970 pDataLine += (a_pData - pCurrLine);
1971 *a_pData = cEndOfLineChar;
1972 SkipNewLine(a_pData);
1973 *pDataLine++ = '\n';
1974 }
1975
1976 // if we didn't find a comment at all then return false
1977 if (a_pVal == a_pData) {
1978 a_pVal = NULL;
1979 return false;
1980 }
1981
1982 // the data (which ends at the end of the last line) needs to be
1983 // null-terminated BEFORE before the newline character(s). If the
1984 // user wants a new line in the multi-line data then they need to
1985 // add an empty line before the tag.
1986 *--pDataLine = '\0';
1987
1988 // if looking for a tag and if we aren't at the end of the data,
1989 // then move a_pData to the start of the next line.
1990 if (a_pTagName && cEndOfLineChar) {
1991 SI_ASSERT(IsNewLineChar(cEndOfLineChar));
1992 *a_pData = cEndOfLineChar;
1993 SkipNewLine(a_pData);
1994 }
1995
1996 return true;
1997}
1998
1999template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2001 const SI_CHAR *&a_pString) {
2002 size_t uLen = 0;
2003 if (sizeof(SI_CHAR) == sizeof(char)) {
2004 uLen = strlen((const char *)a_pString);
2005 } else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
2006 uLen = wcslen((const wchar_t *)a_pString);
2007 } else {
2008 for (; a_pString[uLen]; ++uLen) /*loop*/
2009 ;
2010 }
2011 if (uLen >= (SI_MAX_FILE_SIZE / sizeof(SI_CHAR))) {
2012 return SI_NOMEM;
2013 }
2014 ++uLen; // NULL character
2015 SI_CHAR *pCopy = new (std::nothrow) SI_CHAR[uLen];
2016 if (!pCopy) {
2017 return SI_NOMEM;
2018 }
2019 memcpy(pCopy, a_pString, sizeof(SI_CHAR) * uLen);
2020 m_strings.push_back(pCopy);
2021 a_pString = pCopy;
2022 return SI_OK;
2023}
2024
2025template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2027 UndoIncrementalLoadData(const TNamesDepend &a_oAddedSections,
2028 const TNamesDepend &a_oAddedKeys) {
2029 typename TNamesDepend::const_iterator iAddedKey = a_oAddedKeys.begin();
2030 for (; iAddedKey != a_oAddedKeys.end(); ++iAddedKey) {
2031 Delete(iAddedKey->pComment, iAddedKey->pItem, false);
2032 }
2033
2034 typename TNamesDepend::const_iterator iAddedSection =
2035 a_oAddedSections.begin();
2036 for (; iAddedSection != a_oAddedSections.end(); ++iAddedSection) {
2037 Delete(iAddedSection->pItem, NULL, false);
2038 }
2039}
2040
2041template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2043 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pValue,
2044 const SI_CHAR *a_pComment, bool a_bForceReplace, bool a_bCopyStrings) {
2045 SI_Error rc;
2046 bool bInserted = false;
2047
2048 SI_ASSERT(!a_pComment || IsComment(*a_pComment));
2049
2050 // if we are copying strings then make a copy of the comment now
2051 // because we will need it when we add the entry.
2052 if (a_bCopyStrings && a_pComment) {
2053 rc = CopyString(a_pComment);
2054 if (rc < 0)
2055 return rc;
2056 }
2057
2058 // create the section entry if necessary
2059 typename TSection::iterator iSection = m_data.find(a_pSection);
2060 if (iSection == m_data.end()) {
2061 // if the section doesn't exist then we need a copy as the
2062 // string needs to last beyond the end of this function
2063 if (a_bCopyStrings) {
2064 rc = CopyString(a_pSection);
2065 if (rc < 0)
2066 return rc;
2067 }
2068
2069 // only set the comment if this is a section only entry
2070 Entry oSection(a_pSection, ++m_nOrder);
2071 if (a_pComment && !a_pKey) {
2072 oSection.pComment = a_pComment;
2073 }
2074
2075 typename TSection::value_type oEntry(oSection, TKeyVal());
2076 typedef typename TSection::iterator SectionIterator;
2077 std::pair<SectionIterator, bool> i = m_data.insert(oEntry);
2078 iSection = i.first;
2079 bInserted = true;
2080 }
2081 if (!a_pKey) {
2082 // section only entries are specified with pItem as NULL
2083 return bInserted ? SI_INSERTED : SI_UPDATED;
2084 }
2085
2086 // check for existence of the key
2087 TKeyVal &keyval = iSection->second;
2088 typename TKeyVal::iterator iKey = keyval.find(a_pKey);
2089 bInserted = iKey == keyval.end();
2090
2091 // remove all existing entries but save the load order and
2092 // comment of the first entry
2093 int nLoadOrder = ++m_nOrder;
2094 if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) {
2095 const SI_CHAR *pComment = NULL;
2096 while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) {
2097 if (iKey->first.nOrder < nLoadOrder) {
2098 nLoadOrder = iKey->first.nOrder;
2099 pComment = iKey->first.pComment;
2100 }
2101 ++iKey;
2102 }
2103 if (pComment) {
2104 DeleteString(a_pComment);
2105 a_pComment = pComment;
2106 rc = CopyString(a_pComment);
2107 if (rc < 0)
2108 return rc;
2109 }
2110 Delete(a_pSection, a_pKey);
2111 iKey = keyval.end();
2112 }
2113
2114 // values need to be a valid string, even if they are an empty string
2115 if (!a_pValue) {
2116 a_pValue = &m_cEmptyString;
2117 }
2118
2119 // make string copies if necessary
2120 bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
2121 if (a_bCopyStrings) {
2122 if (bForceCreateNewKey || iKey == keyval.end()) {
2123 // if the key doesn't exist then we need a copy as the
2124 // string needs to last beyond the end of this function
2125 // because we will be inserting the key next
2126 rc = CopyString(a_pKey);
2127 if (rc < 0)
2128 return rc;
2129 }
2130
2131 // we always need a copy of the value
2132 rc = CopyString(a_pValue);
2133 if (rc < 0)
2134 return rc;
2135 }
2136
2137 // create the key entry
2138 if (iKey == keyval.end() || bForceCreateNewKey) {
2139 Entry oKey(a_pKey, nLoadOrder);
2140 if (a_pComment) {
2141 oKey.pComment = a_pComment;
2142 }
2143 typename TKeyVal::value_type oEntry(oKey,
2144 static_cast<const SI_CHAR *>(NULL));
2145 iKey = keyval.insert(oEntry);
2146 } else {
2147 DeleteString(iKey->second);
2148 }
2149
2150 iKey->second = a_pValue;
2151 return bInserted ? SI_INSERTED : SI_UPDATED;
2152}
2153
2154template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2156 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pDefault,
2157 bool *a_pHasMultiple) const {
2158 if (a_pHasMultiple) {
2159 *a_pHasMultiple = false;
2160 }
2161 if (!a_pSection || !a_pKey) {
2162 return a_pDefault;
2163 }
2164 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2165 if (iSection == m_data.end()) {
2166 return a_pDefault;
2167 }
2168 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2169 if (iKeyVal == iSection->second.end()) {
2170 return a_pDefault;
2171 }
2172
2173 // check for multiple entries with the same key
2174 if (m_bAllowMultiKey && a_pHasMultiple) {
2175 typename TKeyVal::const_iterator iTemp = iKeyVal;
2176 if (++iTemp != iSection->second.end()) {
2177 if (!IsLess(a_pKey, iTemp->first.pItem)) {
2178 *a_pHasMultiple = true;
2179 }
2180 }
2181 }
2182
2183 return iKeyVal->second;
2184}
2185
2186template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2188 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, long a_nDefault,
2189 bool *a_pHasMultiple) const {
2190 // return the default if we don't have a value
2191 const SI_CHAR *pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2192 if (!pszValue || !*pszValue)
2193 return a_nDefault;
2194
2195 // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2196 char szValue[64] = {0};
2197 SI_CONVERTER c(m_bStoreIsUtf8);
2198 if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2199 return a_nDefault;
2200 }
2201
2202 // handle the value as hex if prefaced with "0x"
2203 long nValue = a_nDefault;
2204 char *pszSuffix = szValue;
2205 if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
2206 if (!szValue[2])
2207 return a_nDefault;
2208 nValue = strtol(&szValue[2], &pszSuffix, 16);
2209 } else {
2210 nValue = strtol(szValue, &pszSuffix, 10);
2211 }
2212
2213 // any invalid strings will return the default value
2214 if (*pszSuffix) {
2215 return a_nDefault;
2216 }
2217
2218 return nValue;
2219}
2220
2221template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2223 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, long a_nValue,
2224 const SI_CHAR *a_pComment, bool a_bUseHex, bool a_bForceReplace) {
2225 // use SetValue to create sections
2226 if (!a_pSection || !a_pKey)
2227 return SI_FAIL;
2228
2229 // convert to an ASCII string
2230 char szInput[64];
2231#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2232 sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2233#else // !__STDC_WANT_SECURE_LIB__
2234 snprintf(szInput, sizeof(szInput), a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2235#endif // __STDC_WANT_SECURE_LIB__
2236
2237 // convert to output text
2238 SI_CHAR szOutput[64];
2239 SI_CONVERTER c(m_bStoreIsUtf8);
2240 if (!c.ConvertFromStore(szInput, strlen(szInput) + 1, szOutput,
2241 sizeof(szOutput) / sizeof(SI_CHAR))) {
2242 return SI_FAIL;
2243 }
2244
2245 // actually add it
2246 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace,
2247 true);
2248}
2249
2250template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2252 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, double a_nDefault,
2253 bool *a_pHasMultiple) const {
2254 // return the default if we don't have a value
2255 const SI_CHAR *pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2256 if (!pszValue || !*pszValue)
2257 return a_nDefault;
2258
2259 // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2260 char szValue[64] = {0};
2261 SI_CONVERTER c(m_bStoreIsUtf8);
2262 if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2263 return a_nDefault;
2264 }
2265
2266 char *pszSuffix = szValue;
2267 double nValue = strtod(szValue, &pszSuffix);
2268
2269 // any invalid strings will return the default value
2270 // check if no conversion was performed or if there are trailing characters
2271 if (pszSuffix == szValue || *pszSuffix) {
2272 return a_nDefault;
2273 }
2274
2275 return nValue;
2276}
2277
2278template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2280 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, double a_nValue,
2281 const SI_CHAR *a_pComment, bool a_bForceReplace) {
2282 // use SetValue to create sections
2283 if (!a_pSection || !a_pKey)
2284 return SI_FAIL;
2285
2286 // convert to an ASCII string
2287 char szInput[64];
2288#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2289 sprintf_s(szInput, "%f", a_nValue);
2290#else // !__STDC_WANT_SECURE_LIB__
2291 snprintf(szInput, sizeof(szInput), "%f", a_nValue);
2292#endif // __STDC_WANT_SECURE_LIB__
2293
2294 // convert to output text
2295 SI_CHAR szOutput[64];
2296 SI_CONVERTER c(m_bStoreIsUtf8);
2297 if (!c.ConvertFromStore(szInput, strlen(szInput) + 1, szOutput,
2298 sizeof(szOutput) / sizeof(SI_CHAR))) {
2299 return SI_FAIL;
2300 }
2301
2302 // actually add it
2303 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace,
2304 true);
2305}
2306
2307template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2309 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bDefault,
2310 bool *a_pHasMultiple) const {
2311 // return the default if we don't have a value
2312 const SI_CHAR *pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2313 if (!pszValue || !*pszValue)
2314 return a_bDefault;
2315
2316 // we only look at the minimum number of characters
2317 switch (pszValue[0]) {
2318 case 't':
2319 case 'T': // true
2320 case 'y':
2321 case 'Y': // yes
2322 case '1': // 1 (one)
2323 return true;
2324
2325 case 'f':
2326 case 'F': // false
2327 case 'n':
2328 case 'N': // no
2329 case '0': // 0 (zero)
2330 return false;
2331
2332 case 'o':
2333 case 'O':
2334 if (pszValue[1] == 'n' || pszValue[1] == 'N')
2335 return true; // on
2336 if (pszValue[1] == 'f' || pszValue[1] == 'F')
2337 return false; // off
2338 break;
2339 }
2340
2341 // no recognized value, return the default
2342 return a_bDefault;
2343}
2344
2345template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2347 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bValue,
2348 const SI_CHAR *a_pComment, bool a_bForceReplace) {
2349 // use SetValue to create sections
2350 if (!a_pSection || !a_pKey)
2351 return SI_FAIL;
2352
2353 // convert to an ASCII string
2354 const char *pszInput = a_bValue ? "true" : "false";
2355
2356 // convert to output text
2357 SI_CHAR szOutput[64];
2358 SI_CONVERTER c(m_bStoreIsUtf8);
2359 if (!c.ConvertFromStore(pszInput, strlen(pszInput) + 1, szOutput,
2360 sizeof(szOutput) / sizeof(SI_CHAR))) {
2361 return SI_FAIL;
2362 }
2363
2364 // actually add it
2365 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace,
2366 true);
2367}
2368
2369template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2371 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
2372 TNamesDepend &a_values) const {
2373 a_values.clear();
2374
2375 if (!a_pSection || !a_pKey) {
2376 return false;
2377 }
2378 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2379 if (iSection == m_data.end()) {
2380 return false;
2381 }
2382 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2383 if (iKeyVal == iSection->second.end()) {
2384 return false;
2385 }
2386
2387 // insert all values for this key
2388 a_values.push_back(
2389 Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2390 if (m_bAllowMultiKey) {
2391 ++iKeyVal;
2392 while (iKeyVal != iSection->second.end() &&
2393 !IsLess(a_pKey, iKeyVal->first.pItem)) {
2394 a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment,
2395 iKeyVal->first.nOrder));
2396 ++iKeyVal;
2397 }
2398 }
2399
2400 return true;
2401}
2402
2403template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2405 const SI_CHAR *a_pSection) const {
2406 if (!a_pSection) {
2407 return -1;
2408 }
2409
2410 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2411 if (iSection == m_data.end()) {
2412 return -1;
2413 }
2414 const TKeyVal &section = iSection->second;
2415
2416 // if multi-key isn't permitted then the section size is
2417 // the number of keys that we have.
2418 if (!m_bAllowMultiKey || section.empty()) {
2419 return (int)section.size();
2420 }
2421
2422 // otherwise we need to count them
2423 int nCount = 0;
2424 const SI_CHAR *pLastKey = NULL;
2425 typename TKeyVal::const_iterator iKeyVal = section.begin();
2426 for (; iKeyVal != section.end(); ++iKeyVal) {
2427 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2428 ++nCount;
2429 pLastKey = iKeyVal->first.pItem;
2430 }
2431 }
2432 return nCount;
2433}
2434
2435template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2438 const SI_CHAR *a_pSection) const {
2439 if (a_pSection) {
2440 typename TSection::const_iterator i = m_data.find(a_pSection);
2441 if (i != m_data.end()) {
2442 return &(i->second);
2443 }
2444 }
2445 return 0;
2446}
2447
2448template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2450 TNamesDepend &a_names) const {
2451 a_names.clear();
2452 typename TSection::const_iterator i = m_data.begin();
2453 for (; i != m_data.end(); ++i) {
2454 a_names.push_back(i->first);
2455 }
2456}
2457
2458template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2460 const SI_CHAR *a_pSection, TNamesDepend &a_names) const {
2461 a_names.clear();
2462
2463 if (!a_pSection) {
2464 return false;
2465 }
2466
2467 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2468 if (iSection == m_data.end()) {
2469 return false;
2470 }
2471
2472 const TKeyVal &section = iSection->second;
2473 const SI_CHAR *pLastKey = NULL;
2474 typename TKeyVal::const_iterator iKeyVal = section.begin();
2475 for (; iKeyVal != section.end(); ++iKeyVal) {
2476 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2477 a_names.push_back(iKeyVal->first);
2478 pLastKey = iKeyVal->first.pItem;
2479 }
2480 }
2481
2482 return true;
2483}
2484
2485template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2487 const char *a_pszFile, bool a_bAddSignature) const {
2488 FILE *fp = NULL;
2489#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2490 fopen_s(&fp, a_pszFile, "wb");
2491#else // !__STDC_WANT_SECURE_LIB__
2492 fp = fopen(a_pszFile, "wb");
2493#endif // __STDC_WANT_SECURE_LIB__
2494 if (!fp)
2495 return SI_FILE;
2496 SI_Error rc = SaveFile(fp, a_bAddSignature);
2497 fclose(fp);
2498 return rc;
2499}
2500
2501#ifdef SI_HAS_WIDE_FILE
2502template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2504 const SI_WCHAR_T *a_pwszFile, bool a_bAddSignature) const {
2505#ifdef _WIN32
2506 FILE *fp = NULL;
2507#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2508 _wfopen_s(&fp, a_pwszFile, L"wb");
2509#else // !__STDC_WANT_SECURE_LIB__
2510 fp = _wfopen(a_pwszFile, L"wb");
2511#endif // __STDC_WANT_SECURE_LIB__
2512 if (!fp)
2513 return SI_FILE;
2514 SI_Error rc = SaveFile(fp, a_bAddSignature);
2515 fclose(fp);
2516 return rc;
2517#else // !_WIN32 (therefore SI_CONVERT_ICU)
2518 char *szFile = NULL;
2519 SI_Error rcPath = SI_Internal::UCharPathToUtf8(a_pwszFile, szFile);
2520 if (rcPath < 0) {
2521 return rcPath;
2522 }
2523 SI_Error rc = SaveFile(szFile, a_bAddSignature);
2524 delete[] szFile;
2525 return rc;
2526#endif // _WIN32
2527}
2528#endif // SI_HAS_WIDE_FILE
2529
2530template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2532 FILE *a_pFile, bool a_bAddSignature) const {
2533 FileWriter writer(a_pFile);
2534 return Save(writer, a_bAddSignature);
2535}
2536
2537template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2539 OutputWriter &a_oOutput, bool a_bAddSignature) const {
2540 Converter convert(m_bStoreIsUtf8);
2541
2542 // add the UTF-8 signature if it is desired
2543 if (m_bStoreIsUtf8 && a_bAddSignature) {
2544 a_oOutput.Write(SI_UTF8_SIGNATURE);
2545 }
2546
2547 // get all of the sections sorted in load order
2548 TNamesDepend oSections;
2549 GetAllSections(oSections);
2550#if defined(_MSC_VER) && _MSC_VER <= 1200
2551 oSections.sort();
2552#elif defined(__BORLANDC__)
2553 oSections.sort(Entry::LoadOrder());
2554#else
2555 oSections.sort(typename Entry::LoadOrder());
2556#endif
2557
2558 // if there is an empty section name, then it must be written out first
2559 // regardless of the load order
2560 typename TNamesDepend::iterator is = oSections.begin();
2561 for (; is != oSections.end(); ++is) {
2562 if (!*is->pItem) {
2563 // move the empty section name to the front of the section list
2564 if (is != oSections.begin()) {
2565 oSections.splice(oSections.begin(), oSections, is, std::next(is));
2566 }
2567 break;
2568 }
2569 }
2570
2571 // write the file comment if we have one
2572 bool bNeedNewLine = false;
2573 if (m_pFileComment) {
2574 if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
2575 return SI_FAIL;
2576 }
2577 bNeedNewLine = true;
2578 }
2579
2580 // iterate through our sections and output the data
2581 typename TNamesDepend::const_iterator iSection = oSections.begin();
2582 for (; iSection != oSections.end(); ++iSection) {
2583 // write out the comment if there is one
2584 if (iSection->pComment) {
2585 if (bNeedNewLine) {
2586 a_oOutput.Write(SI_NEWLINE_A);
2587 a_oOutput.Write(SI_NEWLINE_A);
2588 }
2589 if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) {
2590 return SI_FAIL;
2591 }
2592 bNeedNewLine = false;
2593 }
2594
2595 if (bNeedNewLine) {
2596 a_oOutput.Write(SI_NEWLINE_A);
2597 a_oOutput.Write(SI_NEWLINE_A);
2598 bNeedNewLine = false;
2599 }
2600
2601 // write the section (unless there is no section name)
2602 if (*iSection->pItem) {
2603 if (!convert.ConvertToStore(iSection->pItem)) {
2604 return SI_FAIL;
2605 }
2606 a_oOutput.Write("[");
2607 a_oOutput.Write(convert.Data());
2608 a_oOutput.Write("]");
2609 a_oOutput.Write(SI_NEWLINE_A);
2610 }
2611
2612 // get all of the keys sorted in load order
2613 TNamesDepend oKeys;
2614 GetAllKeys(iSection->pItem, oKeys);
2615#if defined(_MSC_VER) && _MSC_VER <= 1200
2616 oKeys.sort();
2617#elif defined(__BORLANDC__)
2618 oKeys.sort(Entry::LoadOrder());
2619#else
2620 oKeys.sort(typename Entry::LoadOrder());
2621#endif
2622
2623 // write all keys and values
2624 typename TNamesDepend::const_iterator iKey = oKeys.begin();
2625 for (; iKey != oKeys.end(); ++iKey) {
2626 // get all values for this key
2627 TNamesDepend oValues;
2628 GetAllValues(iSection->pItem, iKey->pItem, oValues);
2629
2630 typename TNamesDepend::const_iterator iValue = oValues.begin();
2631 for (; iValue != oValues.end(); ++iValue) {
2632 // write out the comment if there is one
2633 if (iValue->pComment) {
2634 a_oOutput.Write(SI_NEWLINE_A);
2635 if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) {
2636 return SI_FAIL;
2637 }
2638 }
2639
2640 // write the key
2641 if (!convert.ConvertToStore(iKey->pItem)) {
2642 return SI_FAIL;
2643 }
2644 a_oOutput.Write(convert.Data());
2645
2646 // write the value as long
2647 if (*iValue->pItem || !m_bAllowKeyOnly) {
2648 if (!convert.ConvertToStore(iValue->pItem)) {
2649 return SI_FAIL;
2650 }
2651 a_oOutput.Write(m_bSpaces ? " = " : "=");
2652 if (m_bParseQuotes && IsSingleLineQuotedValue(iValue->pItem)) {
2653 // the only way to preserve external whitespace on a value (i.e. before or after)
2654 // is to quote it. This is simple quoting, we don't escape quotes within the data.
2655 a_oOutput.Write("\"");
2656 a_oOutput.Write(convert.Data());
2657 a_oOutput.Write("\"");
2658 } else if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
2659 // multi-line data needs to be processed specially to ensure
2660 // that we use the correct newline format for the current system
2661 a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
2662 if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
2663 return SI_FAIL;
2664 }
2665 a_oOutput.Write("END_OF_TEXT");
2666 } else {
2667 a_oOutput.Write(convert.Data());
2668 }
2669 }
2670 a_oOutput.Write(SI_NEWLINE_A);
2671 }
2672 }
2673
2674 bNeedNewLine = true;
2675 }
2676
2677 return SI_OK;
2678}
2679
2680template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2682 OutputWriter &a_oOutput, Converter &a_oConverter,
2683 const SI_CHAR *a_pText) const {
2684 const SI_CHAR *pEndOfLine;
2685 SI_CHAR cEndOfLineChar = *a_pText;
2686 while (cEndOfLineChar) {
2687 // find the end of this line
2688 pEndOfLine = a_pText;
2689 for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/
2690 ;
2691 cEndOfLineChar = *pEndOfLine;
2692
2693 const std::basic_string<SI_CHAR> line(
2694 a_pText, static_cast<size_t>(pEndOfLine - a_pText));
2695 if (!a_oConverter.ConvertToStore(line.c_str())) {
2696 return false;
2697 }
2698 a_pText += (pEndOfLine - a_pText) + 1;
2699 a_oOutput.Write(a_oConverter.Data());
2700 a_oOutput.Write(SI_NEWLINE_A);
2701 }
2702 return true;
2703}
2704
2705template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2707 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bRemoveEmpty) {
2708 return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty);
2709}
2710
2711template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2713 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pValue,
2714 bool a_bRemoveEmpty) {
2715 if (!a_pSection) {
2716 return false;
2717 }
2718
2719 typename TSection::iterator iSection = m_data.find(a_pSection);
2720 if (iSection == m_data.end()) {
2721 return false;
2722 }
2723
2724 // remove a single key if we have a keyname
2725 if (a_pKey) {
2726 typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
2727 if (iKeyVal == iSection->second.end()) {
2728 return false;
2729 }
2730
2731 const static SI_STRLESS isLess = SI_STRLESS();
2732
2733 // remove any copied strings and then the key
2734 typename TKeyVal::iterator iDelete;
2735 bool bDeleted = false;
2736 do {
2737 iDelete = iKeyVal++;
2738
2739 if (a_pValue == NULL || (isLess(a_pValue, iDelete->second) == false &&
2740 isLess(iDelete->second, a_pValue) == false)) {
2741 DeleteString(iDelete->first.pItem);
2742 DeleteString(iDelete->first.pComment);
2743 DeleteString(iDelete->second);
2744 iSection->second.erase(iDelete);
2745 bDeleted = true;
2746 }
2747 } while (iKeyVal != iSection->second.end() &&
2748 !IsLess(a_pKey, iKeyVal->first.pItem));
2749
2750 if (!bDeleted) {
2751 return false;
2752 }
2753
2754 // done now if the section is not empty or we are not pruning away
2755 // the empty sections. Otherwise let it fall through into the section
2756 // deletion code
2757 if (!a_bRemoveEmpty || !iSection->second.empty()) {
2758 return true;
2759 }
2760 } else {
2761 // delete all copied strings from this section. The actual
2762 // entries will be removed when the section is removed.
2763 typename TKeyVal::iterator iKeyVal = iSection->second.begin();
2764 for (; iKeyVal != iSection->second.end(); ++iKeyVal) {
2765 DeleteString(iKeyVal->first.pItem);
2766 DeleteString(iKeyVal->first.pComment);
2767 DeleteString(iKeyVal->second);
2768 }
2769 }
2770
2771 // delete the section itself
2772 DeleteString(iSection->first.pItem);
2773 DeleteString(iSection->first.pComment);
2774 m_data.erase(iSection);
2775
2776 return true;
2777}
2778
2779template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2781 const SI_CHAR *a_pString) {
2782 if (!a_pString) {
2783 return;
2784 }
2785 // strings may exist either inside the data block, or they will be
2786 // individually allocated and stored in m_strings. We only physically
2787 // delete those stored in m_strings.
2788 if (!m_pData || a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
2789 typename TNamesDepend::iterator i = m_strings.begin();
2790 for (; i != m_strings.end(); ++i) {
2791 if (a_pString == i->pItem) {
2792 delete[] const_cast<SI_CHAR *>(i->pItem);
2793 m_strings.erase(i);
2794 break;
2795 }
2796 }
2797 }
2798}
2799
2800// ---------------------------------------------------------------------------
2801// CONVERSION FUNCTIONS
2802// ---------------------------------------------------------------------------
2803
2804// Defines the conversion classes for different libraries. Before including
2805// SimpleIni.h, set the converter that you wish you use by defining one of the
2806// following symbols.
2807//
2808// SI_NO_CONVERSION Do not make the "W" wide character version of the
2809// library available. Only CSimpleIniA etc is defined.
2810// Default on Linux/MacOS/etc.
2811// SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
2812// Default on Windows.
2813// SI_CONVERT_GENERIC Use built-in locale-independent UTF-8 conversion
2814// (no additional source files required)
2815// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
2816// ICU headers on include path and icuuc.lib
2817
2818#if !defined(SI_NO_CONVERSION) && !defined(SI_CONVERT_GENERIC) && \
2819 !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
2820#ifdef _WIN32
2821#define SI_CONVERT_WIN32
2822#else
2823#define SI_NO_CONVERSION
2824#endif
2825#endif
2826
2832template <class SI_CHAR> struct SI_GenericCase {
2833 bool operator()(const SI_CHAR *pLeft, const SI_CHAR *pRight) const {
2834 long cmp;
2835 for (; *pLeft && *pRight; ++pLeft, ++pRight) {
2836 cmp = (long)*pLeft - (long)*pRight;
2837 if (cmp != 0) {
2838 return cmp < 0;
2839 }
2840 }
2841 return *pRight != 0;
2842 }
2843};
2844
2851template <class SI_CHAR> struct SI_GenericNoCase {
2852 inline SI_CHAR locase(SI_CHAR ch) const {
2853 return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
2854 }
2855 bool operator()(const SI_CHAR *pLeft, const SI_CHAR *pRight) const {
2856 long cmp;
2857 for (; *pLeft && *pRight; ++pLeft, ++pRight) {
2858 cmp = (long)locase(*pLeft) - (long)locase(*pRight);
2859 if (cmp != 0) {
2860 return cmp < 0;
2861 }
2862 }
2863 return *pRight != 0;
2864 }
2865};
2866
2870template <class SI_CHAR> class SI_ConvertA {
2871 bool m_bStoreIsUtf8;
2872
2873protected:
2874 SI_ConvertA() {}
2875
2876public:
2877 SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) {}
2878
2879 /* copy and assignment */
2880 SI_ConvertA(const SI_ConvertA &rhs) { operator=(rhs); }
2881 SI_ConvertA &operator=(const SI_ConvertA &rhs) {
2882 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2883 return *this;
2884 }
2885
2899 size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen) {
2900 (void)a_pInputData;
2901 SI_ASSERT(a_uInputDataLen != (size_t)-1);
2902
2903 // ASCII/MBCS/UTF-8 needs no conversion
2904 return a_uInputDataLen;
2905 }
2906
2920 bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen,
2921 SI_CHAR *a_pOutputData, size_t a_uOutputDataSize) {
2922 // ASCII/MBCS/UTF-8 needs no conversion
2923 if (a_uInputDataLen > a_uOutputDataSize) {
2924 return false;
2925 }
2926 memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
2927 return true;
2928 }
2929
2940 size_t SizeToStore(const SI_CHAR *a_pInputData) {
2941 // ASCII/MBCS/UTF-8 needs no conversion
2942 return strlen((const char *)a_pInputData) + 1;
2943 }
2944
2958 bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData,
2959 size_t a_uOutputDataSize) {
2960 // calc input string length (SI_CHAR type and size independent)
2961 size_t uInputLen = strlen((const char *)a_pInputData) + 1;
2962 if (uInputLen > a_uOutputDataSize) {
2963 return false;
2964 }
2965
2966 // ascii/UTF-8 needs no conversion
2967 memcpy(a_pOutputData, a_pInputData, uInputLen);
2968 return true;
2969 }
2970};
2971
2972// ---------------------------------------------------------------------------
2973// SI_CONVERT_GENERIC
2974// ---------------------------------------------------------------------------
2975#ifdef SI_CONVERT_GENERIC
2976
2977#define SI_Case SI_GenericCase
2978#define SI_NoCase SI_GenericNoCase
2979
2980#include <wchar.h>
2981
2982namespace SI_UTF8 {
2983
2984constexpr char32_t REPLACEMENT = 0xFFFD;
2985
2987inline bool Decode(const char *&a_src, const char *a_end, char32_t &a_cp) {
2988 if (a_src >= a_end) {
2989 return false;
2990 }
2991 const unsigned char c0 = (unsigned char)*a_src++;
2992 if (c0 < 0x80) {
2993 a_cp = c0;
2994 return true;
2995 }
2996 if (c0 < 0xC2) {
2997 return false;
2998 }
2999
3000 int extra = 0;
3001 char32_t ch = 0;
3002 if (c0 < 0xE0) {
3003 extra = 1;
3004 ch = c0 & 0x1F;
3005 } else if (c0 < 0xF0) {
3006 extra = 2;
3007 ch = c0 & 0x0F;
3008 } else if (c0 < 0xF5) {
3009 extra = 3;
3010 ch = c0 & 0x07;
3011 } else {
3012 return false;
3013 }
3014 if (a_src + extra > a_end) {
3015 a_src -= 1;
3016 return false;
3017 }
3018
3019 for (int i = 0; i < extra; ++i) {
3020 const unsigned char cx = (unsigned char)*a_src++;
3021 if ((cx & 0xC0) != 0x80) {
3022 a_src -= (i + 2);
3023 return false;
3024 }
3025 ch = (ch << 6) | (cx & 0x3F);
3026 }
3027
3028 if ((extra == 1 && ch < 0x80) || (extra == 2 && ch < 0x800) ||
3029 (extra == 3 && ch < 0x10000) || ch > 0x10FFFF ||
3030 (ch >= 0xD800 && ch <= 0xDFFF)) {
3031 a_src -= (extra + 1);
3032 return false;
3033 }
3034
3035 a_cp = ch;
3036 return true;
3037}
3038
3042inline int Encode(char32_t a_cp, char *a_dst, size_t a_cap) {
3043 if (a_cp >= 0x110000 || (a_cp >= 0xD800 && a_cp <= 0xDFFF)) {
3044 a_cp = REPLACEMENT;
3045 }
3046
3047 if (a_cp < 0x80) {
3048 if (a_cap < 1)
3049 return -1;
3050 a_dst[0] = (char)a_cp;
3051 return 1;
3052 }
3053 if (a_cp < 0x800) {
3054 if (a_cap < 2)
3055 return -1;
3056 a_dst[0] = (char)(0xC0 | (a_cp >> 6));
3057 a_dst[1] = (char)(0x80 | (a_cp & 0x3F));
3058 return 2;
3059 }
3060 if (a_cp < 0x10000) {
3061 if (a_cap < 3)
3062 return -1;
3063 a_dst[0] = (char)(0xE0 | (a_cp >> 12));
3064 a_dst[1] = (char)(0x80 | ((a_cp >> 6) & 0x3F));
3065 a_dst[2] = (char)(0x80 | (a_cp & 0x3F));
3066 return 3;
3067 }
3068 if (a_cap < 4)
3069 return -1;
3070 a_dst[0] = (char)(0xF0 | (a_cp >> 18));
3071 a_dst[1] = (char)(0x80 | ((a_cp >> 12) & 0x3F));
3072 a_dst[2] = (char)(0x80 | ((a_cp >> 6) & 0x3F));
3073 a_dst[3] = (char)(0x80 | (a_cp & 0x3F));
3074 return 4;
3075}
3076
3077template <class SI_CHAR>
3078inline bool AppendCodePoint(char32_t a_cp, SI_CHAR *&a_dst, SI_CHAR *a_dstEnd) {
3079 if (sizeof(SI_CHAR) >= sizeof(char32_t)) {
3080 if (a_dst >= a_dstEnd)
3081 return false;
3082 *a_dst++ = (SI_CHAR)a_cp;
3083 return true;
3084 }
3085 if (sizeof(SI_CHAR) == 2) {
3086 if (a_cp < 0x10000) {
3087 if (a_dst >= a_dstEnd)
3088 return false;
3089 *a_dst++ = (SI_CHAR)a_cp;
3090 return true;
3091 }
3092 if (a_dst + 1 >= a_dstEnd)
3093 return false;
3094 a_cp -= 0x10000;
3095 *a_dst++ = (SI_CHAR)(0xD800 + (a_cp >> 10));
3096 *a_dst++ = (SI_CHAR)(0xDC00 + (a_cp & 0x3FF));
3097 return true;
3098 }
3099 return false;
3100}
3101
3102template <class SI_CHAR>
3103inline bool ReadCodePoint(const SI_CHAR *&a_src, char32_t &a_cp) {
3104 if (sizeof(SI_CHAR) >= sizeof(char32_t)) {
3105 a_cp = (char32_t)*a_src++;
3106 return true;
3107 }
3108 if (sizeof(SI_CHAR) == 2) {
3109 const char32_t w = (char32_t)*a_src++;
3110 if (w >= 0xD800 && w <= 0xDBFF) {
3111 if (*a_src >= 0xDC00 && *a_src <= 0xDFFF) {
3112 a_cp = 0x10000 + ((w - 0xD800) << 10) + ((char32_t)*a_src++ - 0xDC00);
3113 return true;
3114 }
3115 }
3116 a_cp = w;
3117 return true;
3118 }
3119 return false;
3120}
3121
3122} // namespace SI_UTF8
3123
3128template <class SI_CHAR> class SI_ConvertW {
3129 bool m_bStoreIsUtf8;
3130
3131protected:
3132 SI_ConvertW() {}
3133
3134public:
3135 SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) {}
3136
3137 /* copy and assignment */
3138 SI_ConvertW(const SI_ConvertW &rhs) { operator=(rhs); }
3139 SI_ConvertW &operator=(const SI_ConvertW &rhs) {
3140 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
3141 return *this;
3142 }
3143
3157 size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen) {
3158 SI_ASSERT(a_uInputDataLen != (size_t)-1);
3159
3160 if (m_bStoreIsUtf8) {
3161 // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
3162 // so we just return the same number of characters required as for
3163 // the source text.
3164 return a_uInputDataLen;
3165 }
3166
3167 // get the required buffer size
3168#if defined(_MSC_VER)
3169 size_t uBufSiz;
3170 errno_t e = mbstowcs_s(&uBufSiz, NULL, 0, a_pInputData, a_uInputDataLen);
3171 return (e == 0) ? uBufSiz : (size_t)-1;
3172#elif !defined(SI_NO_MBSTOWCS_NULL)
3173 return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
3174#else
3175 // fall back processing for platforms that don't support a NULL dest to mbstowcs
3176 // worst case scenario is 1:1, this will be a sufficient buffer size
3177 (void)a_pInputData;
3178 return a_uInputDataLen;
3179#endif
3180 }
3181
3195 bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen,
3196 SI_CHAR *a_pOutputData, size_t a_uOutputDataSize) {
3197 if (m_bStoreIsUtf8) {
3198 const char *src = a_pInputData;
3199 const char *srcEnd = a_pInputData + a_uInputDataLen;
3200 SI_CHAR *dst = a_pOutputData;
3201 SI_CHAR *dstEnd = a_pOutputData + a_uOutputDataSize;
3202 if (sizeof(SI_CHAR) != 2 && sizeof(SI_CHAR) < sizeof(char32_t)) {
3203 return false;
3204 }
3205 while (src < srcEnd) {
3206 char32_t cp;
3207 if (!SI_UTF8::Decode(src, srcEnd, cp)) {
3208 return false;
3209 }
3210 if (!SI_UTF8::AppendCodePoint(cp, dst, dstEnd)) {
3211 return false;
3212 }
3213 }
3214 return true;
3215 }
3216
3217 // convert to wchar_t
3218#if defined(_MSC_VER)
3219 size_t uBufSiz;
3220 errno_t e = mbstowcs_s(&uBufSiz, a_pOutputData, a_uOutputDataSize,
3221 a_pInputData, a_uInputDataLen);
3222 (void)uBufSiz;
3223 return (e == 0);
3224#else
3225 size_t retval = mbstowcs(a_pOutputData, a_pInputData, a_uOutputDataSize);
3226 return retval != (size_t)(-1);
3227#endif
3228 }
3229
3240 size_t SizeToStore(const SI_CHAR *a_pInputData) {
3241 if (m_bStoreIsUtf8) {
3242 // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
3243 size_t uLen = 0;
3244 while (a_pInputData[uLen]) {
3245 ++uLen;
3246 }
3247 return (6 * uLen) + 1;
3248 } else {
3249#if defined(_MSC_VER)
3250 size_t uLen = 0;
3251 errno_t e = wcstombs_s(&uLen, NULL, 0, a_pInputData, 0);
3252 if (e != 0) {
3253 return (size_t)-1;
3254 }
3255 return uLen + 1; // include NULL terminator
3256#else
3257 size_t uLen = wcstombs(NULL, a_pInputData, 0);
3258 if (uLen == (size_t)(-1)) {
3259 return uLen;
3260 }
3261 return uLen + 1; // include NULL terminator
3262#endif
3263 }
3264 }
3265
3279 bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData,
3280 size_t a_uOutputDataSize) {
3281 if (m_bStoreIsUtf8) {
3282 const SI_CHAR *src = a_pInputData;
3283 char *dst = a_pOutputData;
3284 char *dstEnd = a_pOutputData + a_uOutputDataSize;
3285 if (sizeof(SI_CHAR) != 2 && sizeof(SI_CHAR) < sizeof(char32_t)) {
3286 return false;
3287 }
3288 while (*src) {
3289 char32_t cp;
3290 if (!SI_UTF8::ReadCodePoint(src, cp)) {
3291 return false;
3292 }
3293 char buf[4];
3294 const int n = SI_UTF8::Encode(cp, buf, sizeof(buf));
3295 if (n < 0 || (size_t)(dstEnd - dst) < (size_t)n) {
3296 return false;
3297 }
3298 memcpy(dst, buf, (size_t)n);
3299 dst += n;
3300 }
3301 if (dst >= dstEnd) {
3302 return false;
3303 }
3304 *dst = '\0';
3305 return true;
3306 } else {
3307#if defined(_MSC_VER)
3308 size_t converted = 0;
3309 errno_t e = wcstombs_s(&converted, a_pOutputData, a_uOutputDataSize,
3310 a_pInputData, 0);
3311 return (e == 0);
3312#else
3313 size_t retval = wcstombs(a_pOutputData, a_pInputData, a_uOutputDataSize);
3314 return retval != (size_t)-1;
3315#endif
3316 }
3317 }
3318};
3319
3320#endif // SI_CONVERT_GENERIC
3321
3322// ---------------------------------------------------------------------------
3323// SI_CONVERT_ICU
3324// ---------------------------------------------------------------------------
3325#ifdef SI_CONVERT_ICU
3326
3327#define SI_Case SI_GenericCase
3328#define SI_NoCase SI_GenericNoCase
3329
3330#include <unicode/ucnv.h>
3331
3335template <class SI_CHAR> class SI_ConvertW {
3336 const char *m_pEncoding;
3337 UConverter *m_pConverter;
3338
3339protected:
3340 SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) {}
3341
3342public:
3343 SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
3344 m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
3345 }
3346
3347 /* copy and assignment */
3348 SI_ConvertW(const SI_ConvertW &rhs) { operator=(rhs); }
3349 SI_ConvertW &operator=(const SI_ConvertW &rhs) {
3350 m_pEncoding = rhs.m_pEncoding;
3351 m_pConverter = NULL;
3352 return *this;
3353 }
3354 ~SI_ConvertW() {
3355 if (m_pConverter)
3356 ucnv_close(m_pConverter);
3357 }
3358
3372 size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen) {
3373 SI_ASSERT(a_uInputDataLen != (size_t)-1);
3374
3375 UErrorCode nError;
3376
3377 if (!m_pConverter) {
3378 nError = U_ZERO_ERROR;
3379 m_pConverter = ucnv_open(m_pEncoding, &nError);
3380 if (U_FAILURE(nError)) {
3381 return (size_t)-1;
3382 }
3383 }
3384
3385 nError = U_ZERO_ERROR;
3386 int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0, a_pInputData,
3387 (int32_t)a_uInputDataLen, &nError);
3388 if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3389 return (size_t)-1;
3390 }
3391
3392 return (size_t)nLen;
3393 }
3394
3408 bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen,
3409 UChar *a_pOutputData, size_t a_uOutputDataSize) {
3410 UErrorCode nError;
3411
3412 if (!m_pConverter) {
3413 nError = U_ZERO_ERROR;
3414 m_pConverter = ucnv_open(m_pEncoding, &nError);
3415 if (U_FAILURE(nError)) {
3416 return false;
3417 }
3418 }
3419
3420 nError = U_ZERO_ERROR;
3421 ucnv_toUChars(m_pConverter, a_pOutputData, (int32_t)a_uOutputDataSize,
3422 a_pInputData, (int32_t)a_uInputDataLen, &nError);
3423 if (U_FAILURE(nError)) {
3424 return false;
3425 }
3426
3427 return true;
3428 }
3429
3440 size_t SizeToStore(const UChar *a_pInputData) {
3441 UErrorCode nError;
3442
3443 if (!m_pConverter) {
3444 nError = U_ZERO_ERROR;
3445 m_pConverter = ucnv_open(m_pEncoding, &nError);
3446 if (U_FAILURE(nError)) {
3447 return (size_t)-1;
3448 }
3449 }
3450
3451 nError = U_ZERO_ERROR;
3452 int32_t nLen =
3453 ucnv_fromUChars(m_pConverter, NULL, 0, a_pInputData, -1, &nError);
3454 if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3455 return (size_t)-1;
3456 }
3457
3458 return (size_t)nLen + 1;
3459 }
3460
3474 bool ConvertToStore(const UChar *a_pInputData, char *a_pOutputData,
3475 size_t a_uOutputDataSize) {
3476 UErrorCode nError;
3477
3478 if (!m_pConverter) {
3479 nError = U_ZERO_ERROR;
3480 m_pConverter = ucnv_open(m_pEncoding, &nError);
3481 if (U_FAILURE(nError)) {
3482 return false;
3483 }
3484 }
3485
3486 nError = U_ZERO_ERROR;
3487 ucnv_fromUChars(m_pConverter, a_pOutputData, (int32_t)a_uOutputDataSize,
3488 a_pInputData, -1, &nError);
3489 if (U_FAILURE(nError)) {
3490 return false;
3491 }
3492
3493 return true;
3494 }
3495};
3496
3497#endif // SI_CONVERT_ICU
3498
3499// ---------------------------------------------------------------------------
3500// SI_CONVERT_WIN32
3501// ---------------------------------------------------------------------------
3502#ifdef SI_CONVERT_WIN32
3503
3504#define SI_Case SI_GenericCase
3505
3506// Windows CE doesn't have errno or MBCS libraries
3507#ifdef _WIN32_WCE
3508#ifndef SI_NO_MBCS
3509#define SI_NO_MBCS
3510#endif
3511#endif
3512
3513#include <windows.h>
3514#ifdef SI_NO_MBCS
3515#define SI_NoCase SI_GenericNoCase
3516#else // !SI_NO_MBCS
3525#include <mbstring.h>
3526template <class SI_CHAR> struct SI_NoCase {
3527 bool operator()(const SI_CHAR *pLeft, const SI_CHAR *pRight) const {
3528 if (sizeof(SI_CHAR) == sizeof(char)) {
3529 return _mbsicmp((const unsigned char *)pLeft,
3530 (const unsigned char *)pRight) < 0;
3531 }
3532 if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
3533 return _wcsicmp((const wchar_t *)pLeft, (const wchar_t *)pRight) < 0;
3534 }
3536 }
3537};
3538#endif // SI_NO_MBCS
3539
3546template <class SI_CHAR> class SI_ConvertW {
3548
3549protected:
3550 SI_ConvertW() {}
3551
3552public:
3555 }
3556
3557 /* copy and assignment */
3558 SI_ConvertW(const SI_ConvertW &rhs) { operator=(rhs); }
3559 SI_ConvertW &operator=(const SI_ConvertW &rhs) {
3560 m_uCodePage = rhs.m_uCodePage;
3561 return *this;
3562 }
3563
3577 size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen) {
3578 SI_ASSERT(a_uInputDataLen != (size_t)-1);
3579
3581 (int)a_uInputDataLen, 0, 0);
3582 return (size_t)(retval > 0 ? retval : -1);
3583 }
3584
3598 bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen,
3600 int nSize =
3602 (wchar_t *)a_pOutputData, (int)a_uOutputDataSize);
3603 return (nSize > 0);
3604 }
3605
3616 size_t SizeToStore(const SI_CHAR *a_pInputData) {
3618 m_uCodePage, 0, (const wchar_t *)a_pInputData, -1, 0, 0, 0, 0);
3619 return (size_t)(retval > 0 ? retval : -1);
3620 }
3621
3635 bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData,
3636 size_t a_uOutputDataSize) {
3637 int retval =
3638 WideCharToMultiByte(m_uCodePage, 0, (const wchar_t *)a_pInputData, -1,
3639 a_pOutputData, (int)a_uOutputDataSize, 0, 0);
3640 return retval > 0;
3641 }
3642};
3643
3644#endif // SI_CONVERT_WIN32
3645
3646// ---------------------------------------------------------------------------
3647// SI_NO_CONVERSION
3648// ---------------------------------------------------------------------------
3649#ifdef SI_NO_CONVERSION
3650
3651#define SI_Case SI_GenericCase
3652#define SI_NoCase SI_GenericNoCase
3653
3654#endif // SI_NO_CONVERSION
3655
3656// ---------------------------------------------------------------------------
3657// TYPE DEFINITIONS
3658// ---------------------------------------------------------------------------
3659
3662
3663#if defined(SI_NO_CONVERSION)
3664// if there is no wide char conversion then we don't need to define the
3665// widechar "W" versions of CSimpleIni
3666#define CSimpleIni CSimpleIniA
3667#define CSimpleIniCase CSimpleIniCaseA
3668#define SI_NEWLINE SI_NEWLINE_A
3669#else
3670#if defined(SI_CONVERT_ICU)
3675#else
3680#endif
3681
3682#ifdef _UNICODE
3683#define CSimpleIni CSimpleIniW
3684#define CSimpleIniCase CSimpleIniCaseW
3685#define SI_NEWLINE SI_NEWLINE_W
3686#else // !_UNICODE
3687#define CSimpleIni CSimpleIniA
3688#define CSimpleIniCase CSimpleIniCaseA
3689#define SI_NEWLINE SI_NEWLINE_A
3690#endif // _UNICODE
3691#endif
3692
3693#ifdef _MSC_VER
3694#pragma warning(pop)
3695#endif
3696
3697#endif // INCLUDED_SimpleIni_h
Definition SimpleIni.h:444
Definition SimpleIni.h:401
Definition SimpleIni.h:389
Definition SimpleIni.h:414
Definition SimpleIni.h:327
bool DeleteValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pValue, bool a_bRemoveEmpty=false)
Definition SimpleIni.h:2712
bool GetAllKeys(const SI_CHAR *a_pSection, TNamesDepend &a_names) const
Definition SimpleIni.h:2459
int GetSectionSize(const SI_CHAR *a_pSection) const
Definition SimpleIni.h:2404
void SetAllowKeyOnly(bool a_bAllowKeyOnly=true)
Definition SimpleIni.h:586
SI_Error LoadData(const std::string &a_strData)
Definition SimpleIni.h:646
bool UsingQuotes() const
Definition SimpleIni.h:576
std::map< Entry, TKeyVal, typename Entry::KeyOrder > TSection
Definition SimpleIni.h:379
std::list< Entry > TNamesDepend
Definition SimpleIni.h:384
bool IsUnicode() const
Definition SimpleIni.h:515
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)
Definition SimpleIni.h:2346
bool IsMultiKey() const
Definition SimpleIni.h:540
Converter GetConverter() const
Definition SimpleIni.h:1107
bool IsEmpty() const
Definition SimpleIni.h:490
std::multimap< Entry, const SI_CHAR *, typename Entry::KeyOrder > TKeyVal
Definition SimpleIni.h:376
const TKeyVal * GetSection(const SI_CHAR *a_pSection) const
Definition SimpleIni.h:2437
SI_Error Save(OutputWriter &a_oOutput, bool a_bAddSignature=false) const
Definition SimpleIni.h:2538
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
Definition SimpleIni.h:2155
bool GetBoolValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bDefault=false, bool *a_pHasMultiple=NULL) const
Definition SimpleIni.h:2308
long GetLongValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, long a_nDefault=0, bool *a_pHasMultiple=NULL) const
Definition SimpleIni.h:2187
void GetAllSections(TNamesDepend &a_names) const
Definition SimpleIni.h:2449
void Reset()
Definition SimpleIni.h:1266
~CSimpleIniTempl()
Definition SimpleIni.h:1261
bool UsingSpaces() const
Definition SimpleIni.h:565
SI_Error Save(std::string &a_sBuffer, bool a_bAddSignature=false) const
Definition SimpleIni.h:768
bool KeyExists(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey) const
Definition SimpleIni.h:863
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)
Definition SimpleIni.h:972
bool SectionExists(const SI_CHAR *a_pSection) const
Definition SimpleIni.h:858
bool Delete(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bRemoveEmpty=false)
Definition SimpleIni.h:2706
void SetMultiLine(bool a_bAllowMultiLine=true)
Definition SimpleIni.h:549
void SetQuotes(bool a_bParseQuotes=true)
Definition SimpleIni.h:571
void SetUnicode(bool a_bIsUtf8=true)
Definition SimpleIni.h:509
double GetDoubleValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, double a_nDefault=0, bool *a_pHasMultiple=NULL) const
Definition SimpleIni.h:2251
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)
Definition SimpleIni.h:2222
void SetMultiKey(bool a_bAllowMultiKey=true)
Definition SimpleIni.h:535
bool GetAllValues(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, TNamesDepend &a_values) const
Definition SimpleIni.h:2370
void SetSpaces(bool a_bSpaces=true)
Definition SimpleIni.h:562
SI_Error LoadFile(const char *a_pszFile)
Definition SimpleIni.h:1384
bool GetAllowKeyOnly() const
Definition SimpleIni.h:591
SI_Error SaveFile(const char *a_pszFile, bool a_bAddSignature=true) const
Definition SimpleIni.h:2486
CSimpleIniTempl(bool a_bIsUtf8=false, bool a_bMultiKey=false, bool a_bMultiLine=false)
Definition SimpleIni.h:1253
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)
Definition SimpleIni.h:2279
bool IsMultiLine() const
Definition SimpleIni.h:554
Definition SimpleIni.h:2870
bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData, size_t a_uOutputDataSize)
Definition SimpleIni.h:2958
size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen)
Definition SimpleIni.h:2899
size_t SizeToStore(const SI_CHAR *a_pInputData)
Definition SimpleIni.h:2940
bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen, SI_CHAR *a_pOutputData, size_t a_uOutputDataSize)
Definition SimpleIni.h:2920
Definition SimpleIni.h:356
Definition SimpleIni.h:364
Definition SimpleIni.h:332
Definition SimpleIni.h:2832
Definition SimpleIni.h:2851