SimpleIni
Loading...
Searching...
No Matches
SimpleIni.h
1
214#ifndef INCLUDED_SimpleIni_h
215#define INCLUDED_SimpleIni_h
216
217#if defined(_MSC_VER) && (_MSC_VER >= 1020)
218# pragma once
219#endif
220
221// Disable these warnings in MSVC:
222// 4127 "conditional expression is constant" as the conversion classes trigger
223// it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
224// be optimized away in a release build.
225// 4503 'insert' : decorated name length exceeded, name was truncated
226// 4702 "unreachable code" as the MS STL header causes it in release mode.
227// Again, the code causing the warning will be cleaned up by the compiler.
228// 4786 "identifier truncated to 256 characters" as this is thrown hundreds
229// of times VC6 as soon as STL is used.
230#ifdef _MSC_VER
231# pragma warning (push)
232# pragma warning (disable: 4127 4503 4702 4786)
233#endif
234
235#include <cstring>
236#include <cstdlib>
237#include <string>
238#include <map>
239#include <list>
240#include <algorithm>
241#include <stdio.h>
242
243#ifdef SI_SUPPORT_IOSTREAMS
244# include <iostream>
245#endif // SI_SUPPORT_IOSTREAMS
246
247#ifdef _DEBUG
248# ifndef assert
249# include <cassert>
250# endif
251# define SI_ASSERT(x) assert(x)
252#else
253# define SI_ASSERT(x)
254#endif
255
256using SI_Error = int;
257
258constexpr int SI_OK = 0;
259constexpr int SI_UPDATED = 1;
260constexpr int SI_INSERTED = 2;
261
262// note: test for any error with (retval < 0)
263constexpr int SI_FAIL = -1;
264constexpr int SI_NOMEM = -2;
265constexpr int SI_FILE = -3;
266
269constexpr size_t SI_MAX_FILE_SIZE = 1024ULL * 1024ULL * 1024ULL;
270
271#define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
272
273#ifdef _WIN32
274# define SI_NEWLINE_A "\r\n"
275# define SI_NEWLINE_W L"\r\n"
276#else // !_WIN32
277# define SI_NEWLINE_A "\n"
278# define SI_NEWLINE_W L"\n"
279#endif // _WIN32
280
281#if defined(SI_CONVERT_ICU)
282# include <unicode/ustring.h>
283#endif
284
285#if defined(_WIN32)
286# define SI_HAS_WIDE_FILE
287# define SI_WCHAR_T wchar_t
288#elif defined(SI_CONVERT_ICU)
289# define SI_HAS_WIDE_FILE
290# define SI_WCHAR_T UChar
291#endif
292
293
294// ---------------------------------------------------------------------------
295// MAIN TEMPLATE CLASS
296// ---------------------------------------------------------------------------
297
317template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
319{
320public:
321 typedef SI_CHAR SI_CHAR_T;
322
324 struct Entry {
325 const SI_CHAR * pItem;
326 const SI_CHAR * pComment;
327 int nOrder;
328
329 Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0)
330 : pItem(a_pszItem)
331 , pComment(NULL)
332 , nOrder(a_nOrder)
333 { }
334 Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder)
335 : pItem(a_pszItem)
336 , pComment(a_pszComment)
337 , nOrder(a_nOrder)
338 { }
339 Entry(const Entry & rhs) { operator=(rhs); }
340 Entry & operator=(const Entry & rhs) {
341 pItem = rhs.pItem;
342 pComment = rhs.pComment;
343 nOrder = rhs.nOrder;
344 return *this;
345 }
346
347#if defined(_MSC_VER) && _MSC_VER <= 1200
349 bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); }
350 bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); }
351#endif
352
354 struct KeyOrder {
355 bool operator()(const Entry & lhs, const Entry & rhs) const {
356 const static SI_STRLESS isLess = SI_STRLESS();
357 return isLess(lhs.pItem, rhs.pItem);
358 }
359 };
360
362 struct LoadOrder {
363 bool operator()(const Entry & lhs, const Entry & rhs) const {
364 if (lhs.nOrder != rhs.nOrder) {
365 return lhs.nOrder < rhs.nOrder;
366 }
367 return KeyOrder()(lhs, rhs);
368 }
369 };
370 };
371
373 typedef std::multimap<Entry,const SI_CHAR *,typename Entry::KeyOrder> TKeyVal;
374
376 typedef std::map<Entry,TKeyVal,typename Entry::KeyOrder> TSection;
377
381 typedef std::list<Entry> TNamesDepend;
382
387 public:
388 OutputWriter() { }
389 virtual ~OutputWriter() { }
390 virtual void Write(const char * a_pBuf) = 0;
391 private:
392 OutputWriter(const OutputWriter &); // disable
393 OutputWriter & operator=(const OutputWriter &); // disable
394 };
395
397 class FileWriter : public OutputWriter {
398 FILE * m_file;
399 public:
400 FileWriter(FILE * a_file) : m_file(a_file) { }
401 void Write(const char * a_pBuf) {
402 fputs(a_pBuf, m_file);
403 }
404 private:
405 FileWriter(const FileWriter &); // disable
406 FileWriter & operator=(const FileWriter &); // disable
407 };
408
410 class StringWriter : public OutputWriter {
411 std::string & m_string;
412 public:
413 StringWriter(std::string & a_string) : m_string(a_string) { }
414 void Write(const char * a_pBuf) {
415 m_string.append(a_pBuf);
416 }
417 private:
418 StringWriter(const StringWriter &); // disable
419 StringWriter & operator=(const StringWriter &); // disable
420 };
421
422#ifdef SI_SUPPORT_IOSTREAMS
424 class StreamWriter : public OutputWriter {
425 std::ostream & m_ostream;
426 public:
427 StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { }
428 void Write(const char * a_pBuf) {
429 m_ostream << a_pBuf;
430 }
431 private:
432 StreamWriter(const StreamWriter &); // disable
433 StreamWriter & operator=(const StreamWriter &); // disable
434 };
435#endif // SI_SUPPORT_IOSTREAMS
436
440 class Converter : private SI_CONVERTER {
441 public:
442 Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) {
443 m_scratch.resize(1024);
444 }
445 Converter(const Converter & rhs) { operator=(rhs); }
446 Converter & operator=(const Converter & rhs) {
447 m_scratch = rhs.m_scratch;
448 return *this;
449 }
450 bool ConvertToStore(const SI_CHAR * a_pszString) {
451 size_t uLen = SI_CONVERTER::SizeToStore(a_pszString);
452 if (uLen == (size_t)(-1)) {
453 return false;
454 }
455 while (uLen > m_scratch.size()) {
456 m_scratch.resize(m_scratch.size() * 2);
457 }
458 return SI_CONVERTER::ConvertToStore(
459 a_pszString,
460 const_cast<char*>(m_scratch.data()),
461 m_scratch.size());
462 }
463 const char * Data() { return m_scratch.data(); }
464 private:
465 std::string m_scratch;
466 };
467
468public:
469 /*-----------------------------------------------------------------------*/
470
478 bool a_bIsUtf8 = false,
479 bool a_bMultiKey = false,
480 bool a_bMultiLine = false
481 );
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) m_bStoreIsUtf8 = a_bIsUtf8;
511 }
512
514 bool IsUnicode() const { return m_bStoreIsUtf8; }
515
534 void SetMultiKey(bool a_bAllowMultiKey = true) {
535 m_bAllowMultiKey = a_bAllowMultiKey;
536 }
537
539 bool IsMultiKey() const { return m_bAllowMultiKey; }
540
548 void SetMultiLine(bool a_bAllowMultiLine = true) {
549 m_bAllowMultiLine = a_bAllowMultiLine;
550 }
551
553 bool IsMultiLine() const { return m_bAllowMultiLine; }
554
561 void SetSpaces(bool a_bSpaces = true) {
562 m_bSpaces = a_bSpaces;
563 }
564
566 bool UsingSpaces() const { return m_bSpaces; }
567
568
573 void SetQuotes(bool a_bParseQuotes = true) {
574 m_bParseQuotes = a_bParseQuotes;
575 }
576
578 bool UsingQuotes() const { return m_bParseQuotes; }
579
588 void SetAllowKeyOnly(bool a_bAllowKeyOnly = true) {
589 m_bAllowKeyOnly = a_bAllowKeyOnly;
590 }
591
593 bool GetAllowKeyOnly() const { return m_bAllowKeyOnly; }
594
595
596
597 /*-----------------------------------------------------------------------*/
609 SI_Error LoadFile(
610 const char * a_pszFile
611 );
612
613#ifdef SI_HAS_WIDE_FILE
620 SI_Error LoadFile(
621 const SI_WCHAR_T * a_pwszFile
622 );
623#endif // SI_HAS_WIDE_FILE
624
632 SI_Error LoadFile(
633 FILE * a_fpFile
634 );
635
636#ifdef SI_SUPPORT_IOSTREAMS
643 SI_Error LoadData(
644 std::istream & a_istream
645 );
646#endif // SI_SUPPORT_IOSTREAMS
647
654 SI_Error LoadData(const std::string & a_strData) {
655 return LoadData(a_strData.c_str(), a_strData.size());
656 }
657
665 SI_Error LoadData(
666 const char * a_pData,
667 size_t a_uDataLen
668 );
669
670 /*-----------------------------------------------------------------------*/
686 SI_Error SaveFile(
687 const char * a_pszFile,
688 bool a_bAddSignature = true
689 ) const;
690
691#ifdef SI_HAS_WIDE_FILE
702 SI_Error SaveFile(
703 const SI_WCHAR_T * a_pwszFile,
704 bool a_bAddSignature = true
705 ) const;
706#endif // _WIN32
707
720 SI_Error SaveFile(
721 FILE * a_pFile,
722 bool a_bAddSignature = false
723 ) const;
724
756 SI_Error Save(
757 OutputWriter & a_oOutput,
758 bool a_bAddSignature = false
759 ) const;
760
761#ifdef SI_SUPPORT_IOSTREAMS
773 SI_Error Save(
774 std::ostream & a_ostream,
775 bool a_bAddSignature = false
776 ) const
777 {
778 StreamWriter writer(a_ostream);
779 return Save(writer, a_bAddSignature);
780 }
781#endif // SI_SUPPORT_IOSTREAMS
782
794 SI_Error Save(
795 std::string & a_sBuffer,
796 bool a_bAddSignature = false
797 ) const
798 {
799 StringWriter writer(a_sBuffer);
800 return Save(writer, a_bAddSignature);
801 }
802
803 /*-----------------------------------------------------------------------*/
821 void GetAllSections(
822 TNamesDepend & a_names
823 ) const;
824
842 bool GetAllKeys(
843 const SI_CHAR * a_pSection,
844 TNamesDepend & a_names
845 ) const;
846
863 bool GetAllValues(
864 const SI_CHAR * a_pSection,
865 const SI_CHAR * a_pKey,
866 TNamesDepend & a_values
867 ) const;
868
878 int GetSectionSize(
879 const SI_CHAR * a_pSection
880 ) const;
881
895 const TKeyVal * GetSection(
896 const SI_CHAR * a_pSection
897 ) const;
898
900 inline bool SectionExists(
901 const SI_CHAR * a_pSection
902 ) const {
903 return GetSection(a_pSection) != NULL;
904 }
905
907 inline bool KeyExists(
908 const SI_CHAR * a_pSection,
909 const SI_CHAR * a_pKey
910 ) const {
911 return GetValue(a_pSection, a_pKey) != NULL;
912 }
913
931 const SI_CHAR * GetValue(
932 const SI_CHAR * a_pSection,
933 const SI_CHAR * a_pKey,
934 const SI_CHAR * a_pDefault = NULL,
935 bool * a_pHasMultiple = NULL
936 ) const;
937
951 long GetLongValue(
952 const SI_CHAR * a_pSection,
953 const SI_CHAR * a_pKey,
954 long a_nDefault = 0,
955 bool * a_pHasMultiple = NULL
956 ) const;
957
971 double GetDoubleValue(
972 const SI_CHAR * a_pSection,
973 const SI_CHAR * a_pKey,
974 double a_nDefault = 0,
975 bool * a_pHasMultiple = NULL
976 ) const;
977
996 bool GetBoolValue(
997 const SI_CHAR * a_pSection,
998 const SI_CHAR * a_pKey,
999 bool a_bDefault = false,
1000 bool * a_pHasMultiple = NULL
1001 ) const;
1002
1032 SI_Error SetValue(
1033 const SI_CHAR * a_pSection,
1034 const SI_CHAR * a_pKey,
1035 const SI_CHAR * a_pValue,
1036 const SI_CHAR * a_pComment = NULL,
1037 bool a_bForceReplace = false
1038 )
1039 {
1040 return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true);
1041 }
1042
1066 SI_Error SetLongValue(
1067 const SI_CHAR * a_pSection,
1068 const SI_CHAR * a_pKey,
1069 long a_nValue,
1070 const SI_CHAR * a_pComment = NULL,
1071 bool a_bUseHex = false,
1072 bool a_bForceReplace = false
1073 );
1074
1095 SI_Error SetDoubleValue(
1096 const SI_CHAR * a_pSection,
1097 const SI_CHAR * a_pKey,
1098 double a_nValue,
1099 const SI_CHAR * a_pComment = NULL,
1100 bool a_bForceReplace = false
1101 );
1102
1123 SI_Error SetBoolValue(
1124 const SI_CHAR * a_pSection,
1125 const SI_CHAR * a_pKey,
1126 bool a_bValue,
1127 const SI_CHAR * a_pComment = NULL,
1128 bool a_bForceReplace = false
1129 );
1130
1149 bool Delete(
1150 const SI_CHAR * a_pSection,
1151 const SI_CHAR * a_pKey,
1152 bool a_bRemoveEmpty = false
1153 );
1154
1175 bool DeleteValue(
1176 const SI_CHAR * a_pSection,
1177 const SI_CHAR * a_pKey,
1178 const SI_CHAR * a_pValue,
1179 bool a_bRemoveEmpty = false
1180 );
1181
1182 /*-----------------------------------------------------------------------*/
1192 return Converter(m_bStoreIsUtf8);
1193 }
1194
1195 /*-----------------------------------------------------------------------*/
1198private:
1199 // copying is not permitted
1200 CSimpleIniTempl(const CSimpleIniTempl &); // disabled
1201 CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled
1202
1205 SI_Error FindFileComment(
1206 SI_CHAR *& a_pData,
1207 bool a_bCopyStrings
1208 );
1209
1214 bool FindEntry(
1215 SI_CHAR *& a_pData,
1216 const SI_CHAR *& a_pSection,
1217 const SI_CHAR *& a_pKey,
1218 const SI_CHAR *& a_pVal,
1219 const SI_CHAR *& a_pComment
1220 ) const;
1221
1244 SI_Error AddEntry(
1245 const SI_CHAR * a_pSection,
1246 const SI_CHAR * a_pKey,
1247 const SI_CHAR * a_pValue,
1248 const SI_CHAR * a_pComment,
1249 bool a_bForceReplace,
1250 bool a_bCopyStrings
1251 );
1252
1254 inline bool IsSpace(SI_CHAR ch) const {
1255 return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
1256 }
1257
1259 inline bool IsComment(SI_CHAR ch) const {
1260 return (ch == ';' || ch == '#');
1261 }
1262
1263
1265 inline void SkipNewLine(SI_CHAR *& a_pData) const {
1266 a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1;
1267 }
1268
1270 SI_Error CopyString(const SI_CHAR *& a_pString);
1271
1273 void DeleteString(const SI_CHAR * a_pString);
1274
1276 bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const {
1277 const static SI_STRLESS isLess = SI_STRLESS();
1278 return isLess(a_pLeft, a_pRight);
1279 }
1280
1281 bool IsMultiLineTag(const SI_CHAR * a_pData) const;
1282 bool IsMultiLineData(const SI_CHAR * a_pData) const;
1283 bool IsSingleLineQuotedValue(const SI_CHAR* a_pData) const;
1284 bool LoadMultiLineText(
1285 SI_CHAR *& a_pData,
1286 const SI_CHAR *& a_pVal,
1287 const SI_CHAR * a_pTagName,
1288 bool a_bAllowBlankLinesInComment = false
1289 ) const;
1290 bool IsNewLineChar(SI_CHAR a_c) const;
1291
1292 bool OutputMultiLineText(
1293 OutputWriter & a_oOutput,
1294 Converter & a_oConverter,
1295 const SI_CHAR * a_pText
1296 ) const;
1297
1298private:
1304 SI_CHAR * m_pData;
1305
1310 size_t m_uDataLen;
1311
1313 const SI_CHAR * m_pFileComment;
1314
1316 const SI_CHAR m_cEmptyString;
1317
1319 TSection m_data;
1320
1325 TNamesDepend m_strings;
1326
1328 bool m_bStoreIsUtf8;
1329
1331 bool m_bAllowMultiKey;
1332
1334 bool m_bAllowMultiLine;
1335
1337 bool m_bSpaces;
1338
1340 bool m_bParseQuotes;
1341
1343 bool m_bAllowKeyOnly;
1344
1348 int m_nOrder;
1349};
1350
1351// ---------------------------------------------------------------------------
1352// IMPLEMENTATION
1353// ---------------------------------------------------------------------------
1354
1355template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1357 bool a_bIsUtf8,
1358 bool a_bAllowMultiKey,
1359 bool a_bAllowMultiLine
1360 )
1361 : m_pData(0)
1362 , m_uDataLen(0)
1363 , m_pFileComment(NULL)
1364 , m_cEmptyString(0)
1365 , m_bStoreIsUtf8(a_bIsUtf8)
1366 , m_bAllowMultiKey(a_bAllowMultiKey)
1367 , m_bAllowMultiLine(a_bAllowMultiLine)
1368 , m_bSpaces(true)
1369 , m_bParseQuotes(false)
1370 , m_bAllowKeyOnly(false)
1371 , m_nOrder(0)
1372{ }
1373
1374template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1379
1380template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1381void
1383{
1384 // remove all data
1385 delete[] m_pData;
1386 m_pData = NULL;
1387 m_uDataLen = 0;
1388 m_pFileComment = NULL;
1389 if (!m_data.empty()) {
1390 m_data.erase(m_data.begin(), m_data.end());
1391 }
1392
1393 // remove all strings
1394 if (!m_strings.empty()) {
1395 typename TNamesDepend::iterator i = m_strings.begin();
1396 for (; i != m_strings.end(); ++i) {
1397 delete[] const_cast<SI_CHAR*>(i->pItem);
1398 }
1399 m_strings.erase(m_strings.begin(), m_strings.end());
1400 }
1401}
1402
1403template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1404SI_Error
1406 const char * a_pszFile
1407 )
1408{
1409 FILE * fp = NULL;
1410#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1411 fopen_s(&fp, a_pszFile, "rb");
1412#else // !__STDC_WANT_SECURE_LIB__
1413 fp = fopen(a_pszFile, "rb");
1414#endif // __STDC_WANT_SECURE_LIB__
1415 if (!fp) {
1416 return SI_FILE;
1417 }
1418 SI_Error rc = LoadFile(fp);
1419 fclose(fp);
1420 return rc;
1421}
1422
1423#ifdef SI_HAS_WIDE_FILE
1424template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1425SI_Error
1427 const SI_WCHAR_T * a_pwszFile
1428 )
1429{
1430#ifdef _WIN32
1431 FILE * fp = NULL;
1432#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1433 _wfopen_s(&fp, a_pwszFile, L"rb");
1434#else // !__STDC_WANT_SECURE_LIB__
1435 fp = _wfopen(a_pwszFile, L"rb");
1436#endif // __STDC_WANT_SECURE_LIB__
1437 if (!fp) return SI_FILE;
1438 SI_Error rc = LoadFile(fp);
1439 fclose(fp);
1440 return rc;
1441#else // !_WIN32 (therefore SI_CONVERT_ICU)
1442 char szFile[256];
1443 u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
1444 return LoadFile(szFile);
1445#endif // _WIN32
1446}
1447#endif // SI_HAS_WIDE_FILE
1448
1449template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1450SI_Error
1452 FILE * a_fpFile
1453 )
1454{
1455 // load the raw file data
1456 int retval = fseek(a_fpFile, 0, SEEK_END);
1457 if (retval != 0) {
1458 return SI_FILE;
1459 }
1460 long lSize = ftell(a_fpFile);
1461 if (lSize < 0) {
1462 return SI_FILE;
1463 }
1464 if (lSize == 0) {
1465 return SI_OK;
1466 }
1467
1468 // check file size is within supported limits (SI_MAX_FILE_SIZE)
1469 if (static_cast<size_t>(lSize) > SI_MAX_FILE_SIZE) {
1470 return SI_FILE;
1471 }
1472
1473 // allocate and ensure NULL terminated
1474 char * pData = new(std::nothrow) char[static_cast<size_t>(lSize) + 1];
1475 if (!pData) {
1476 return SI_NOMEM;
1477 }
1478 pData[lSize] = 0;
1479
1480 // load data into buffer
1481 fseek(a_fpFile, 0, SEEK_SET);
1482 size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
1483 if (uRead != (size_t) lSize) {
1484 delete[] pData;
1485 return SI_FILE;
1486 }
1487
1488 // convert the raw data to unicode
1489 SI_Error rc = LoadData(pData, uRead);
1490 delete[] pData;
1491 return rc;
1492}
1493
1494template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1495SI_Error
1497 const char * a_pData,
1498 size_t a_uDataLen
1499 )
1500{
1501 if (!a_pData) {
1502 return SI_OK;
1503 }
1504
1505 // if the UTF-8 BOM exists, consume it and set mode to unicode, if we have
1506 // already loaded data and try to change mode half-way through then this will
1507 // be ignored and we will assert in debug versions
1508 if (a_uDataLen >= 3 && memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
1509 a_pData += 3;
1510 a_uDataLen -= 3;
1511 SI_ASSERT(m_bStoreIsUtf8 || !m_pData); // we don't expect mixed mode data
1512 SetUnicode();
1513 }
1514
1515 if (a_uDataLen == 0) {
1516 return SI_OK;
1517 }
1518
1519 // determine the length of the converted data
1520 SI_CONVERTER converter(m_bStoreIsUtf8);
1521 size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
1522 if (uLen == (size_t)(-1)) {
1523 return SI_FAIL;
1524 }
1525
1526 // check converted data size is within supported limits (SI_MAX_FILE_SIZE)
1527 if (uLen >= (SI_MAX_FILE_SIZE / sizeof(SI_CHAR))) {
1528 return SI_FILE;
1529 }
1530
1531 // allocate memory for the data, ensure that there is a NULL
1532 // terminator wherever the converted data ends
1533 SI_CHAR * pData = new(std::nothrow) SI_CHAR[uLen + 1];
1534 if (!pData) {
1535 return SI_NOMEM;
1536 }
1537 memset(pData, 0, sizeof(SI_CHAR) * (uLen + 1));
1538
1539 // convert the data
1540 if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
1541 delete[] pData;
1542 return SI_FAIL;
1543 }
1544
1545 // parse it
1546 const static SI_CHAR empty = 0;
1547 SI_CHAR * pWork = pData;
1548 const SI_CHAR * pSection = &empty;
1549 const SI_CHAR * pItem = NULL;
1550 const SI_CHAR * pVal = NULL;
1551 const SI_CHAR * pComment = NULL;
1552
1553 // We copy the strings if we are loading data into this class when we
1554 // already have stored some.
1555 bool bCopyStrings = (m_pData != NULL);
1556
1557 // find a file comment if it exists, this is a comment that starts at the
1558 // beginning of the file and continues until the first blank line.
1559 SI_Error rc = FindFileComment(pWork, bCopyStrings);
1560 if (rc < 0) return rc;
1561
1562 // add every entry in the file to the data table
1563 while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
1564 rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings);
1565 if (rc < 0) return rc;
1566 }
1567
1568 // store these strings if we didn't copy them
1569 if (bCopyStrings) {
1570 delete[] pData;
1571 }
1572 else {
1573 m_pData = pData;
1574 m_uDataLen = uLen+1;
1575 }
1576
1577 return SI_OK;
1578}
1579
1580#ifdef SI_SUPPORT_IOSTREAMS
1581template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1582SI_Error
1584 std::istream & a_istream
1585 )
1586{
1587 std::string strData;
1588 char szBuf[512];
1589 do {
1590 a_istream.get(szBuf, sizeof(szBuf), '\0');
1591 strData.append(szBuf);
1592 }
1593 while (a_istream.good());
1594 return LoadData(strData);
1595}
1596#endif // SI_SUPPORT_IOSTREAMS
1597
1598template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1599SI_Error
1601 SI_CHAR *& a_pData,
1602 bool a_bCopyStrings
1603 )
1604{
1605 // there can only be a single file comment
1606 if (m_pFileComment) {
1607 return SI_OK;
1608 }
1609
1610 // Load the file comment as multi-line text, this will modify all of
1611 // the newline characters to be single \n chars
1612 if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
1613 return SI_OK;
1614 }
1615
1616 // copy the string if necessary
1617 if (a_bCopyStrings) {
1618 SI_Error rc = CopyString(m_pFileComment);
1619 if (rc < 0) return rc;
1620 }
1621
1622 return SI_OK;
1623}
1624
1625template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1626bool
1628 SI_CHAR *& a_pData,
1629 const SI_CHAR *& a_pSection,
1630 const SI_CHAR *& a_pKey,
1631 const SI_CHAR *& a_pVal,
1632 const SI_CHAR *& a_pComment
1633 ) const
1634{
1635 a_pComment = NULL;
1636
1637 bool bHaveValue = false;
1638 SI_CHAR * pTrail = NULL;
1639 while (*a_pData) {
1640 // skip spaces and empty lines
1641 while (*a_pData && IsSpace(*a_pData)) {
1642 ++a_pData;
1643 }
1644 if (!*a_pData) {
1645 break;
1646 }
1647
1648 // skip processing of comment lines but keep a pointer to
1649 // the start of the comment.
1650 if (IsComment(*a_pData)) {
1651 LoadMultiLineText(a_pData, a_pComment, NULL, true);
1652 continue;
1653 }
1654
1655 // process section names
1656 if (*a_pData == '[') {
1657 // skip leading spaces
1658 ++a_pData;
1659 while (*a_pData && IsSpace(*a_pData)) {
1660 ++a_pData;
1661 }
1662
1663 // find the end of the section name (it may contain spaces)
1664 // and convert it to lowercase as necessary
1665 a_pSection = a_pData;
1666 while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
1667 ++a_pData;
1668 }
1669
1670 // if it's an invalid line, just skip it
1671 if (*a_pData != ']') {
1672 continue;
1673 }
1674
1675 // remove trailing spaces from the section
1676 pTrail = a_pData - 1;
1677 while (pTrail >= a_pSection && IsSpace(*pTrail)) {
1678 --pTrail;
1679 }
1680 ++pTrail;
1681 *pTrail = 0;
1682
1683 // skip to the end of the line
1684 ++a_pData; // safe as checked that it == ']' above
1685 while (*a_pData && !IsNewLineChar(*a_pData)) {
1686 ++a_pData;
1687 }
1688
1689 a_pKey = NULL;
1690 a_pVal = NULL;
1691 return true;
1692 }
1693
1694 // find the end of the key name (it may contain spaces)
1695 a_pKey = a_pData;
1696 while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
1697 ++a_pData;
1698 }
1699 // *a_pData is null, equals, or newline
1700
1701 // if no value and we don't allow no value, then invalid
1702 bHaveValue = (*a_pData == '=');
1703 if (!bHaveValue && !m_bAllowKeyOnly) {
1704 continue;
1705 }
1706
1707 // empty keys are invalid
1708 if (bHaveValue && a_pKey == a_pData) {
1709 while (*a_pData && !IsNewLineChar(*a_pData)) {
1710 ++a_pData;
1711 }
1712 continue;
1713 }
1714
1715 // remove trailing spaces from the key
1716 pTrail = a_pData - 1;
1717 while (pTrail >= a_pKey && IsSpace(*pTrail)) {
1718 --pTrail;
1719 }
1720 ++pTrail;
1721
1722 if (bHaveValue) {
1723 // process the value
1724 *pTrail = 0;
1725
1726 // skip leading whitespace on the value
1727 ++a_pData; // safe as checked that it == '=' above
1728 while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
1729 ++a_pData;
1730 }
1731
1732 // find the end of the value which is the end of this line
1733 a_pVal = a_pData;
1734 while (*a_pData && !IsNewLineChar(*a_pData)) {
1735 ++a_pData;
1736 }
1737
1738 // remove trailing spaces from the value
1739 pTrail = a_pData - 1;
1740 if (*a_pData) { // prepare for the next round
1741 SkipNewLine(a_pData);
1742 }
1743 while (pTrail >= a_pVal && IsSpace(*pTrail)) {
1744 --pTrail;
1745 }
1746 ++pTrail;
1747 *pTrail = 0;
1748
1749 // check for multi-line entries
1750 if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
1751 // skip the "<<<" to get the tag that will end the multiline
1752 const SI_CHAR* pTagName = a_pVal + 3;
1753 return LoadMultiLineText(a_pData, a_pVal, pTagName);
1754 }
1755
1756 // check for quoted values, we are not supporting escapes in quoted values (yet)
1757 if (m_bParseQuotes) {
1758 --pTrail;
1759 if (pTrail > a_pVal && *a_pVal == '"' && *pTrail == '"') {
1760 ++a_pVal;
1761 *pTrail = 0;
1762 }
1763 }
1764 }
1765 else {
1766 // no value to process, just prepare for the next
1767 if (*a_pData) {
1768 SkipNewLine(a_pData);
1769 }
1770 *pTrail = 0;
1771 }
1772
1773 // return the standard entry
1774 return true;
1775 }
1776
1777 return false;
1778}
1779
1780template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1781bool
1783 const SI_CHAR * a_pVal
1784 ) const
1785{
1786 // check for the "<<<" prefix for a multi-line entry
1787 if (*a_pVal++ != '<') return false;
1788 if (*a_pVal++ != '<') return false;
1789 if (*a_pVal++ != '<') return false;
1790 return true;
1791}
1792
1793template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1794bool
1796 const SI_CHAR * a_pData
1797 ) const
1798{
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>
1832bool
1834 const SI_CHAR* a_pData
1835) const
1836{
1837 // data needs quoting if it starts or ends with whitespace
1838 // and doesn't have embedded newlines
1839
1840 // empty string
1841 if (!*a_pData) {
1842 return false;
1843 }
1844
1845 // check for prefix
1846 if (IsSpace(*a_pData)) {
1847 return true;
1848 }
1849
1850 // embedded newlines
1851 const SI_CHAR * pStart = a_pData;
1852 while (*a_pData) {
1853 if (IsNewLineChar(*a_pData)) {
1854 return false;
1855 }
1856 ++a_pData;
1857 }
1858
1859 // check for suffix (ensure we don't go before start of string)
1860 if (a_pData > pStart && IsSpace(*(a_pData - 1))) {
1861 return true;
1862 }
1863
1864 return false;
1865}
1866
1867template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1868bool
1870 SI_CHAR a_c
1871 ) const
1872{
1873 return (a_c == '\n' || a_c == '\r');
1874}
1875
1876template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1877bool
1879 SI_CHAR *& a_pData,
1880 const SI_CHAR *& a_pVal,
1881 const SI_CHAR * a_pTagName,
1882 bool a_bAllowBlankLinesInComment
1883 ) const
1884{
1885 // we modify this data to strip all newlines down to a single '\n'
1886 // character. This means that on Windows we need to strip out some
1887 // characters which will make the data shorter.
1888 // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
1889 // LINE1-LINE1\nLINE2-LINE2\0
1890 // The pDataLine entry is the pointer to the location in memory that
1891 // the current line needs to start to run following the existing one.
1892 // This may be the same as pCurrLine in which case no move is needed.
1893 SI_CHAR * pDataLine = a_pData;
1894 SI_CHAR * pCurrLine;
1895
1896 // value starts at the current line
1897 a_pVal = a_pData;
1898
1899 // find the end tag. This tag must start in column 1 and be
1900 // followed by a newline. We ignore any whitespace after the end
1901 // tag but not whitespace before it.
1902 SI_CHAR cEndOfLineChar = *a_pData;
1903 for(;;) {
1904 // if we are loading comments then we need a comment character as
1905 // the first character on every line
1906 if (!a_pTagName && !IsComment(*a_pData)) {
1907 // if we aren't allowing blank lines then we're done
1908 if (!a_bAllowBlankLinesInComment) {
1909 break;
1910 }
1911
1912 // if we are allowing blank lines then we only include them
1913 // in this comment if another comment follows, so read ahead
1914 // to find out.
1915 SI_CHAR * pCurr = a_pData;
1916 int nNewLines = 0;
1917 while (IsSpace(*pCurr)) {
1918 if (IsNewLineChar(*pCurr)) {
1919 ++nNewLines;
1920 SkipNewLine(pCurr);
1921 }
1922 else {
1923 ++pCurr;
1924 }
1925 }
1926
1927 // we have a comment, add the blank lines to the output
1928 // and continue processing from here
1929 if (IsComment(*pCurr)) {
1930 for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n';
1931 a_pData = pCurr;
1932 continue;
1933 }
1934
1935 // the comment ends here
1936 break;
1937 }
1938
1939 // find the end of this line
1940 pCurrLine = a_pData;
1941 while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
1942
1943 // move this line down to the location that it should be if necessary
1944 if (pDataLine < pCurrLine) {
1945 size_t nLen = (size_t) (a_pData - pCurrLine);
1946 memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
1947 pDataLine[nLen] = '\0';
1948 }
1949
1950 // end the line with a NULL
1951 cEndOfLineChar = *a_pData;
1952 *a_pData = 0;
1953
1954 // if are looking for a tag then do the check now. This is done before
1955 // checking for end of the data, so that if we have the tag at the end
1956 // of the data then the tag is removed correctly.
1957 if (a_pTagName) {
1958 // strip whitespace from the end of this tag
1959 SI_CHAR* pc = a_pData - 1;
1960 while (pc > pDataLine && IsSpace(*pc)) --pc;
1961 SI_CHAR ch = *++pc;
1962 *pc = 0;
1963
1964 if (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)) {
1965 break;
1966 }
1967
1968 *pc = ch;
1969 }
1970
1971 // if we are at the end of the data then we just automatically end
1972 // this entry and return the current data.
1973 if (!cEndOfLineChar) {
1974 return true;
1975 }
1976
1977 // otherwise we need to process this newline to ensure that it consists
1978 // of just a single \n character.
1979 pDataLine += (a_pData - pCurrLine);
1980 *a_pData = cEndOfLineChar;
1981 SkipNewLine(a_pData);
1982 *pDataLine++ = '\n';
1983 }
1984
1985 // if we didn't find a comment at all then return false
1986 if (a_pVal == a_pData) {
1987 a_pVal = NULL;
1988 return false;
1989 }
1990
1991 // the data (which ends at the end of the last line) needs to be
1992 // null-terminated BEFORE before the newline character(s). If the
1993 // user wants a new line in the multi-line data then they need to
1994 // add an empty line before the tag.
1995 *--pDataLine = '\0';
1996
1997 // if looking for a tag and if we aren't at the end of the data,
1998 // then move a_pData to the start of the next line.
1999 if (a_pTagName && cEndOfLineChar) {
2000 SI_ASSERT(IsNewLineChar(cEndOfLineChar));
2001 *a_pData = cEndOfLineChar;
2002 SkipNewLine(a_pData);
2003 }
2004
2005 return true;
2006}
2007
2008template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2009SI_Error
2011 const SI_CHAR *& a_pString
2012 )
2013{
2014 size_t uLen = 0;
2015 if (sizeof(SI_CHAR) == sizeof(char)) {
2016 uLen = strlen((const char *)a_pString);
2017 }
2018 else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
2019 uLen = wcslen((const wchar_t *)a_pString);
2020 }
2021 else {
2022 for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
2023 }
2024 ++uLen; // NULL character
2025 SI_CHAR * pCopy = new(std::nothrow) SI_CHAR[uLen];
2026 if (!pCopy) {
2027 return SI_NOMEM;
2028 }
2029 memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
2030 m_strings.push_back(pCopy);
2031 a_pString = pCopy;
2032 return SI_OK;
2033}
2034
2035template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2036SI_Error
2038 const SI_CHAR * a_pSection,
2039 const SI_CHAR * a_pKey,
2040 const SI_CHAR * a_pValue,
2041 const SI_CHAR * a_pComment,
2042 bool a_bForceReplace,
2043 bool a_bCopyStrings
2044 )
2045{
2046 SI_Error rc;
2047 bool bInserted = false;
2048
2049 SI_ASSERT(!a_pComment || IsComment(*a_pComment));
2050
2051 // if we are copying strings then make a copy of the comment now
2052 // because we will need it when we add the entry.
2053 if (a_bCopyStrings && a_pComment) {
2054 rc = CopyString(a_pComment);
2055 if (rc < 0) 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) return rc;
2066 }
2067
2068 // only set the comment if this is a section only entry
2069 Entry oSection(a_pSection, ++m_nOrder);
2070 if (a_pComment && !a_pKey) {
2071 oSection.pComment = a_pComment;
2072 }
2073
2074 typename TSection::value_type oEntry(oSection, TKeyVal());
2075 typedef typename TSection::iterator SectionIterator;
2076 std::pair<SectionIterator,bool> i = m_data.insert(oEntry);
2077 iSection = i.first;
2078 bInserted = true;
2079 }
2080 if (!a_pKey) {
2081 // section only entries are specified with pItem as NULL
2082 return bInserted ? SI_INSERTED : SI_UPDATED;
2083 }
2084
2085 // check for existence of the key
2086 TKeyVal & keyval = iSection->second;
2087 typename TKeyVal::iterator iKey = keyval.find(a_pKey);
2088 bInserted = iKey == keyval.end();
2089
2090 // remove all existing entries but save the load order and
2091 // comment of the first entry
2092 int nLoadOrder = ++m_nOrder;
2093 if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) {
2094 const SI_CHAR * pComment = NULL;
2095 while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) {
2096 if (iKey->first.nOrder < nLoadOrder) {
2097 nLoadOrder = iKey->first.nOrder;
2098 pComment = iKey->first.pComment;
2099 }
2100 ++iKey;
2101 }
2102 if (pComment) {
2103 DeleteString(a_pComment);
2104 a_pComment = pComment;
2105 rc = CopyString(a_pComment);
2106 if (rc < 0) return rc;
2107 }
2108 Delete(a_pSection, a_pKey);
2109 iKey = keyval.end();
2110 }
2111
2112 // values need to be a valid string, even if they are an empty string
2113 if (!a_pValue) {
2114 a_pValue = &m_cEmptyString;
2115 }
2116
2117 // make string copies if necessary
2118 bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
2119 if (a_bCopyStrings) {
2120 if (bForceCreateNewKey || iKey == keyval.end()) {
2121 // if the key doesn't exist then we need a copy as the
2122 // string needs to last beyond the end of this function
2123 // because we will be inserting the key next
2124 rc = CopyString(a_pKey);
2125 if (rc < 0) return rc;
2126 }
2127
2128 // we always need a copy of the value
2129 rc = CopyString(a_pValue);
2130 if (rc < 0) return rc;
2131 }
2132
2133 // create the key entry
2134 if (iKey == keyval.end() || bForceCreateNewKey) {
2135 Entry oKey(a_pKey, nLoadOrder);
2136 if (a_pComment) {
2137 oKey.pComment = a_pComment;
2138 }
2139 typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL));
2140 iKey = keyval.insert(oEntry);
2141 }
2142
2143 iKey->second = a_pValue;
2144 return bInserted ? SI_INSERTED : SI_UPDATED;
2145}
2146
2147template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2148const SI_CHAR *
2150 const SI_CHAR * a_pSection,
2151 const SI_CHAR * a_pKey,
2152 const SI_CHAR * a_pDefault,
2153 bool * a_pHasMultiple
2154 ) const
2155{
2156 if (a_pHasMultiple) {
2157 *a_pHasMultiple = false;
2158 }
2159 if (!a_pSection || !a_pKey) {
2160 return a_pDefault;
2161 }
2162 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2163 if (iSection == m_data.end()) {
2164 return a_pDefault;
2165 }
2166 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2167 if (iKeyVal == iSection->second.end()) {
2168 return a_pDefault;
2169 }
2170
2171 // check for multiple entries with the same key
2172 if (m_bAllowMultiKey && a_pHasMultiple) {
2173 typename TKeyVal::const_iterator iTemp = iKeyVal;
2174 if (++iTemp != iSection->second.end()) {
2175 if (!IsLess(a_pKey, iTemp->first.pItem)) {
2176 *a_pHasMultiple = true;
2177 }
2178 }
2179 }
2180
2181 return iKeyVal->second;
2182}
2183
2184template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2185long
2187 const SI_CHAR * a_pSection,
2188 const SI_CHAR * a_pKey,
2189 long a_nDefault,
2190 bool * a_pHasMultiple
2191 ) const
2192{
2193 // return the default if we don't have a value
2194 const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2195 if (!pszValue || !*pszValue) return a_nDefault;
2196
2197 // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2198 char szValue[64] = { 0 };
2199 SI_CONVERTER c(m_bStoreIsUtf8);
2200 if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2201 return a_nDefault;
2202 }
2203
2204 // handle the value as hex if prefaced with "0x"
2205 long nValue = a_nDefault;
2206 char * pszSuffix = szValue;
2207 if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
2208 if (!szValue[2]) return a_nDefault;
2209 nValue = strtol(&szValue[2], &pszSuffix, 16);
2210 }
2211 else {
2212 nValue = strtol(szValue, &pszSuffix, 10);
2213 }
2214
2215 // any invalid strings will return the default value
2216 if (*pszSuffix) {
2217 return a_nDefault;
2218 }
2219
2220 return nValue;
2221}
2222
2223template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2224SI_Error
2226 const SI_CHAR * a_pSection,
2227 const SI_CHAR * a_pKey,
2228 long a_nValue,
2229 const SI_CHAR * a_pComment,
2230 bool a_bUseHex,
2231 bool a_bForceReplace
2232 )
2233{
2234 // use SetValue to create sections
2235 if (!a_pSection || !a_pKey) return SI_FAIL;
2236
2237 // convert to an ASCII string
2238 char szInput[64];
2239#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2240 sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2241#else // !__STDC_WANT_SECURE_LIB__
2242 snprintf(szInput, sizeof(szInput), a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2243#endif // __STDC_WANT_SECURE_LIB__
2244
2245 // convert to output text
2246 SI_CHAR szOutput[64];
2247 SI_CONVERTER c(m_bStoreIsUtf8);
2248 c.ConvertFromStore(szInput, strlen(szInput) + 1,
2249 szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2250
2251 // actually add it
2252 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2253}
2254
2255template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2256double
2258 const SI_CHAR * a_pSection,
2259 const SI_CHAR * a_pKey,
2260 double a_nDefault,
2261 bool * a_pHasMultiple
2262 ) const
2263{
2264 // return the default if we don't have a value
2265 const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2266 if (!pszValue || !*pszValue) return a_nDefault;
2267
2268 // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2269 char szValue[64] = { 0 };
2270 SI_CONVERTER c(m_bStoreIsUtf8);
2271 if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2272 return a_nDefault;
2273 }
2274
2275 char * pszSuffix = szValue;
2276 double nValue = strtod(szValue, &pszSuffix);
2277
2278 // any invalid strings will return the default value
2279 // check if no conversion was performed or if there are trailing characters
2280 if (pszSuffix == szValue || *pszSuffix) {
2281 return a_nDefault;
2282 }
2283
2284 return nValue;
2285}
2286
2287template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2288SI_Error
2290 const SI_CHAR * a_pSection,
2291 const SI_CHAR * a_pKey,
2292 double a_nValue,
2293 const SI_CHAR * a_pComment,
2294 bool a_bForceReplace
2295 )
2296{
2297 // use SetValue to create sections
2298 if (!a_pSection || !a_pKey) return SI_FAIL;
2299
2300 // convert to an ASCII string
2301 char szInput[64];
2302#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2303 sprintf_s(szInput, "%f", a_nValue);
2304#else // !__STDC_WANT_SECURE_LIB__
2305 snprintf(szInput, sizeof(szInput), "%f", a_nValue);
2306#endif // __STDC_WANT_SECURE_LIB__
2307
2308 // convert to output text
2309 SI_CHAR szOutput[64];
2310 SI_CONVERTER c(m_bStoreIsUtf8);
2311 c.ConvertFromStore(szInput, strlen(szInput) + 1,
2312 szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2313
2314 // actually add it
2315 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2316}
2317
2318template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2319bool
2321 const SI_CHAR * a_pSection,
2322 const SI_CHAR * a_pKey,
2323 bool a_bDefault,
2324 bool * a_pHasMultiple
2325 ) const
2326{
2327 // return the default if we don't have a value
2328 const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2329 if (!pszValue || !*pszValue) return a_bDefault;
2330
2331 // we only look at the minimum number of characters
2332 switch (pszValue[0]) {
2333 case 't': case 'T': // true
2334 case 'y': case 'Y': // yes
2335 case '1': // 1 (one)
2336 return true;
2337
2338 case 'f': case 'F': // false
2339 case 'n': case 'N': // no
2340 case '0': // 0 (zero)
2341 return false;
2342
2343 case 'o': case 'O':
2344 if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on
2345 if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off
2346 break;
2347 }
2348
2349 // no recognized value, return the default
2350 return a_bDefault;
2351}
2352
2353template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2354SI_Error
2356 const SI_CHAR * a_pSection,
2357 const SI_CHAR * a_pKey,
2358 bool a_bValue,
2359 const SI_CHAR * a_pComment,
2360 bool a_bForceReplace
2361 )
2362{
2363 // use SetValue to create sections
2364 if (!a_pSection || !a_pKey) return SI_FAIL;
2365
2366 // convert to an ASCII string
2367 const char * pszInput = a_bValue ? "true" : "false";
2368
2369 // convert to output text
2370 SI_CHAR szOutput[64];
2371 SI_CONVERTER c(m_bStoreIsUtf8);
2372 c.ConvertFromStore(pszInput, strlen(pszInput) + 1,
2373 szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2374
2375 // actually add it
2376 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2377}
2378
2379template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2380bool
2382 const SI_CHAR * a_pSection,
2383 const SI_CHAR * a_pKey,
2384 TNamesDepend & a_values
2385 ) const
2386{
2387 a_values.clear();
2388
2389 if (!a_pSection || !a_pKey) {
2390 return false;
2391 }
2392 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2393 if (iSection == m_data.end()) {
2394 return false;
2395 }
2396 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2397 if (iKeyVal == iSection->second.end()) {
2398 return false;
2399 }
2400
2401 // insert all values for this key
2402 a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2403 if (m_bAllowMultiKey) {
2404 ++iKeyVal;
2405 while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {
2406 a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2407 ++iKeyVal;
2408 }
2409 }
2410
2411 return true;
2412}
2413
2414template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2415int
2417 const SI_CHAR * a_pSection
2418 ) const
2419{
2420 if (!a_pSection) {
2421 return -1;
2422 }
2423
2424 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2425 if (iSection == m_data.end()) {
2426 return -1;
2427 }
2428 const TKeyVal & section = iSection->second;
2429
2430 // if multi-key isn't permitted then the section size is
2431 // the number of keys that we have.
2432 if (!m_bAllowMultiKey || section.empty()) {
2433 return (int) section.size();
2434 }
2435
2436 // otherwise we need to count them
2437 int nCount = 0;
2438 const SI_CHAR * pLastKey = NULL;
2439 typename TKeyVal::const_iterator iKeyVal = section.begin();
2440 for (; iKeyVal != section.end(); ++iKeyVal) {
2441 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2442 ++nCount;
2443 pLastKey = iKeyVal->first.pItem;
2444 }
2445 }
2446 return nCount;
2447}
2448
2449template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2452 const SI_CHAR * a_pSection
2453 ) const
2454{
2455 if (a_pSection) {
2456 typename TSection::const_iterator i = m_data.find(a_pSection);
2457 if (i != m_data.end()) {
2458 return &(i->second);
2459 }
2460 }
2461 return 0;
2462}
2463
2464template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2465void
2467 TNamesDepend & a_names
2468 ) const
2469{
2470 a_names.clear();
2471 typename TSection::const_iterator i = m_data.begin();
2472 for (; i != m_data.end(); ++i) {
2473 a_names.push_back(i->first);
2474 }
2475}
2476
2477template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2478bool
2480 const SI_CHAR * a_pSection,
2481 TNamesDepend & a_names
2482 ) const
2483{
2484 a_names.clear();
2485
2486 if (!a_pSection) {
2487 return false;
2488 }
2489
2490 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2491 if (iSection == m_data.end()) {
2492 return false;
2493 }
2494
2495 const TKeyVal & section = iSection->second;
2496 const SI_CHAR * pLastKey = NULL;
2497 typename TKeyVal::const_iterator iKeyVal = section.begin();
2498 for (; iKeyVal != section.end(); ++iKeyVal) {
2499 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2500 a_names.push_back(iKeyVal->first);
2501 pLastKey = iKeyVal->first.pItem;
2502 }
2503 }
2504
2505 return true;
2506}
2507
2508template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2509SI_Error
2511 const char * a_pszFile,
2512 bool a_bAddSignature
2513 ) const
2514{
2515 FILE * fp = NULL;
2516#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2517 fopen_s(&fp, a_pszFile, "wb");
2518#else // !__STDC_WANT_SECURE_LIB__
2519 fp = fopen(a_pszFile, "wb");
2520#endif // __STDC_WANT_SECURE_LIB__
2521 if (!fp) return SI_FILE;
2522 SI_Error rc = SaveFile(fp, a_bAddSignature);
2523 fclose(fp);
2524 return rc;
2525}
2526
2527#ifdef SI_HAS_WIDE_FILE
2528template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2529SI_Error
2531 const SI_WCHAR_T * a_pwszFile,
2532 bool a_bAddSignature
2533 ) const
2534{
2535#ifdef _WIN32
2536 FILE * fp = NULL;
2537#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2538 _wfopen_s(&fp, a_pwszFile, L"wb");
2539#else // !__STDC_WANT_SECURE_LIB__
2540 fp = _wfopen(a_pwszFile, L"wb");
2541#endif // __STDC_WANT_SECURE_LIB__
2542 if (!fp) return SI_FILE;
2543 SI_Error rc = SaveFile(fp, a_bAddSignature);
2544 fclose(fp);
2545 return rc;
2546#else // !_WIN32 (therefore SI_CONVERT_ICU)
2547 char szFile[256];
2548 u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
2549 return SaveFile(szFile, a_bAddSignature);
2550#endif // _WIN32
2551}
2552#endif // SI_HAS_WIDE_FILE
2553
2554template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2555SI_Error
2557 FILE * a_pFile,
2558 bool a_bAddSignature
2559 ) const
2560{
2561 FileWriter writer(a_pFile);
2562 return Save(writer, a_bAddSignature);
2563}
2564
2565template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2566SI_Error
2568 OutputWriter & a_oOutput,
2569 bool a_bAddSignature
2570 ) const
2571{
2572 Converter convert(m_bStoreIsUtf8);
2573
2574 // add the UTF-8 signature if it is desired
2575 if (m_bStoreIsUtf8 && a_bAddSignature) {
2576 a_oOutput.Write(SI_UTF8_SIGNATURE);
2577 }
2578
2579 // get all of the sections sorted in load order
2580 TNamesDepend oSections;
2581 GetAllSections(oSections);
2582#if defined(_MSC_VER) && _MSC_VER <= 1200
2583 oSections.sort();
2584#elif defined(__BORLANDC__)
2585 oSections.sort(Entry::LoadOrder());
2586#else
2587 oSections.sort(typename Entry::LoadOrder());
2588#endif
2589
2590 // if there is an empty section name, then it must be written out first
2591 // regardless of the load order
2592 typename TNamesDepend::iterator is = oSections.begin();
2593 for (; is != oSections.end(); ++is) {
2594 if (!*is->pItem) {
2595 // move the empty section name to the front of the section list
2596 if (is != oSections.begin()) {
2597 oSections.splice(oSections.begin(), oSections, is, std::next(is));
2598 }
2599 break;
2600 }
2601 }
2602
2603 // write the file comment if we have one
2604 bool bNeedNewLine = false;
2605 if (m_pFileComment) {
2606 if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
2607 return SI_FAIL;
2608 }
2609 bNeedNewLine = true;
2610 }
2611
2612 // iterate through our sections and output the data
2613 typename TNamesDepend::const_iterator iSection = oSections.begin();
2614 for ( ; iSection != oSections.end(); ++iSection ) {
2615 // write out the comment if there is one
2616 if (iSection->pComment) {
2617 if (bNeedNewLine) {
2618 a_oOutput.Write(SI_NEWLINE_A);
2619 a_oOutput.Write(SI_NEWLINE_A);
2620 }
2621 if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) {
2622 return SI_FAIL;
2623 }
2624 bNeedNewLine = false;
2625 }
2626
2627 if (bNeedNewLine) {
2628 a_oOutput.Write(SI_NEWLINE_A);
2629 a_oOutput.Write(SI_NEWLINE_A);
2630 bNeedNewLine = false;
2631 }
2632
2633 // write the section (unless there is no section name)
2634 if (*iSection->pItem) {
2635 if (!convert.ConvertToStore(iSection->pItem)) {
2636 return SI_FAIL;
2637 }
2638 a_oOutput.Write("[");
2639 a_oOutput.Write(convert.Data());
2640 a_oOutput.Write("]");
2641 a_oOutput.Write(SI_NEWLINE_A);
2642 }
2643
2644 // get all of the keys sorted in load order
2645 TNamesDepend oKeys;
2646 GetAllKeys(iSection->pItem, oKeys);
2647#if defined(_MSC_VER) && _MSC_VER <= 1200
2648 oKeys.sort();
2649#elif defined(__BORLANDC__)
2650 oKeys.sort(Entry::LoadOrder());
2651#else
2652 oKeys.sort(typename Entry::LoadOrder());
2653#endif
2654
2655 // write all keys and values
2656 typename TNamesDepend::const_iterator iKey = oKeys.begin();
2657 for ( ; iKey != oKeys.end(); ++iKey) {
2658 // get all values for this key
2659 TNamesDepend oValues;
2660 GetAllValues(iSection->pItem, iKey->pItem, oValues);
2661
2662 typename TNamesDepend::const_iterator iValue = oValues.begin();
2663 for ( ; iValue != oValues.end(); ++iValue) {
2664 // write out the comment if there is one
2665 if (iValue->pComment) {
2666 a_oOutput.Write(SI_NEWLINE_A);
2667 if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) {
2668 return SI_FAIL;
2669 }
2670 }
2671
2672 // write the key
2673 if (!convert.ConvertToStore(iKey->pItem)) {
2674 return SI_FAIL;
2675 }
2676 a_oOutput.Write(convert.Data());
2677
2678 // write the value as long
2679 if (*iValue->pItem || !m_bAllowKeyOnly) {
2680 if (!convert.ConvertToStore(iValue->pItem)) {
2681 return SI_FAIL;
2682 }
2683 a_oOutput.Write(m_bSpaces ? " = " : "=");
2684 if (m_bParseQuotes && IsSingleLineQuotedValue(iValue->pItem)) {
2685 // the only way to preserve external whitespace on a value (i.e. before or after)
2686 // is to quote it. This is simple quoting, we don't escape quotes within the data.
2687 a_oOutput.Write("\"");
2688 a_oOutput.Write(convert.Data());
2689 a_oOutput.Write("\"");
2690 }
2691 else if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
2692 // multi-line data needs to be processed specially to ensure
2693 // that we use the correct newline format for the current system
2694 a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
2695 if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
2696 return SI_FAIL;
2697 }
2698 a_oOutput.Write("END_OF_TEXT");
2699 }
2700 else {
2701 a_oOutput.Write(convert.Data());
2702 }
2703 }
2704 a_oOutput.Write(SI_NEWLINE_A);
2705 }
2706 }
2707
2708 bNeedNewLine = true;
2709 }
2710
2711 return SI_OK;
2712}
2713
2714template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2715bool
2717 OutputWriter & a_oOutput,
2718 Converter & a_oConverter,
2719 const SI_CHAR * a_pText
2720 ) const
2721{
2722 const SI_CHAR * pEndOfLine;
2723 SI_CHAR cEndOfLineChar = *a_pText;
2724 while (cEndOfLineChar) {
2725 // find the end of this line
2726 pEndOfLine = a_pText;
2727 for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;
2728 cEndOfLineChar = *pEndOfLine;
2729
2730 // temporarily null terminate, convert and output the line
2731 *const_cast<SI_CHAR*>(pEndOfLine) = 0;
2732 if (!a_oConverter.ConvertToStore(a_pText)) {
2733 return false;
2734 }
2735 *const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar;
2736 a_pText += (pEndOfLine - a_pText) + 1;
2737 a_oOutput.Write(a_oConverter.Data());
2738 a_oOutput.Write(SI_NEWLINE_A);
2739 }
2740 return true;
2741}
2742
2743template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2744bool
2746 const SI_CHAR * a_pSection,
2747 const SI_CHAR * a_pKey,
2748 bool a_bRemoveEmpty
2749 )
2750{
2751 return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty);
2752}
2753
2754template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2755bool
2757 const SI_CHAR * a_pSection,
2758 const SI_CHAR * a_pKey,
2759 const SI_CHAR * a_pValue,
2760 bool a_bRemoveEmpty
2761 )
2762{
2763 if (!a_pSection) {
2764 return false;
2765 }
2766
2767 typename TSection::iterator iSection = m_data.find(a_pSection);
2768 if (iSection == m_data.end()) {
2769 return false;
2770 }
2771
2772 // remove a single key if we have a keyname
2773 if (a_pKey) {
2774 typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
2775 if (iKeyVal == iSection->second.end()) {
2776 return false;
2777 }
2778
2779 const static SI_STRLESS isLess = SI_STRLESS();
2780
2781 // remove any copied strings and then the key
2782 typename TKeyVal::iterator iDelete;
2783 bool bDeleted = false;
2784 do {
2785 iDelete = iKeyVal++;
2786
2787 if(a_pValue == NULL ||
2788 (isLess(a_pValue, iDelete->second) == false &&
2789 isLess(iDelete->second, a_pValue) == false)) {
2790 DeleteString(iDelete->first.pItem);
2791 DeleteString(iDelete->second);
2792 iSection->second.erase(iDelete);
2793 bDeleted = true;
2794 }
2795 }
2796 while (iKeyVal != iSection->second.end()
2797 && !IsLess(a_pKey, iKeyVal->first.pItem));
2798
2799 if(!bDeleted) {
2800 return false;
2801 }
2802
2803 // done now if the section is not empty or we are not pruning away
2804 // the empty sections. Otherwise let it fall through into the section
2805 // deletion code
2806 if (!a_bRemoveEmpty || !iSection->second.empty()) {
2807 return true;
2808 }
2809 }
2810 else {
2811 // delete all copied strings from this section. The actual
2812 // entries will be removed when the section is removed.
2813 typename TKeyVal::iterator iKeyVal = iSection->second.begin();
2814 for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
2815 DeleteString(iKeyVal->first.pItem);
2816 DeleteString(iKeyVal->second);
2817 }
2818 }
2819
2820 // delete the section itself
2821 DeleteString(iSection->first.pItem);
2822 m_data.erase(iSection);
2823
2824 return true;
2825}
2826
2827template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2828void
2830 const SI_CHAR * a_pString
2831 )
2832{
2833 // strings may exist either inside the data block, or they will be
2834 // individually allocated and stored in m_strings. We only physically
2835 // delete those stored in m_strings.
2836 if (!m_pData || a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
2837 typename TNamesDepend::iterator i = m_strings.begin();
2838 for (;i != m_strings.end(); ++i) {
2839 if (a_pString == i->pItem) {
2840 delete[] const_cast<SI_CHAR*>(i->pItem);
2841 m_strings.erase(i);
2842 break;
2843 }
2844 }
2845 }
2846}
2847
2848// ---------------------------------------------------------------------------
2849// CONVERSION FUNCTIONS
2850// ---------------------------------------------------------------------------
2851
2852// Defines the conversion classes for different libraries. Before including
2853// SimpleIni.h, set the converter that you wish you use by defining one of the
2854// following symbols.
2855//
2856// SI_NO_CONVERSION Do not make the "W" wide character version of the
2857// library available. Only CSimpleIniA etc is defined.
2858// Default on Linux/MacOS/etc.
2859// SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
2860// Default on Windows.
2861// SI_CONVERT_GENERIC Use the Unicode reference conversion library in
2862// the accompanying files ConvertUTF.h/c
2863// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
2864// ICU headers on include path and icuuc.lib
2865
2866#if !defined(SI_NO_CONVERSION) && !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
2867# ifdef _WIN32
2868# define SI_CONVERT_WIN32
2869# else
2870# define SI_NO_CONVERSION
2871# endif
2872#endif
2873
2879template<class SI_CHAR>
2881 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2882 long cmp;
2883 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2884 cmp = (long) *pLeft - (long) *pRight;
2885 if (cmp != 0) {
2886 return cmp < 0;
2887 }
2888 }
2889 return *pRight != 0;
2890 }
2891};
2892
2899template<class SI_CHAR>
2901 inline SI_CHAR locase(SI_CHAR ch) const {
2902 return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
2903 }
2904 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2905 long cmp;
2906 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2907 cmp = (long) locase(*pLeft) - (long) locase(*pRight);
2908 if (cmp != 0) {
2909 return cmp < 0;
2910 }
2911 }
2912 return *pRight != 0;
2913 }
2914};
2915
2919template<class SI_CHAR>
2921 bool m_bStoreIsUtf8;
2922protected:
2923 SI_ConvertA() { }
2924public:
2925 SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
2926
2927 /* copy and assignment */
2928 SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }
2929 SI_ConvertA & operator=(const SI_ConvertA & rhs) {
2930 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2931 return *this;
2932 }
2933
2948 const char * a_pInputData,
2949 size_t a_uInputDataLen)
2950 {
2951 (void)a_pInputData;
2952 SI_ASSERT(a_uInputDataLen != (size_t) -1);
2953
2954 // ASCII/MBCS/UTF-8 needs no conversion
2955 return a_uInputDataLen;
2956 }
2957
2972 const char * a_pInputData,
2973 size_t a_uInputDataLen,
2974 SI_CHAR * a_pOutputData,
2975 size_t a_uOutputDataSize)
2976 {
2977 // ASCII/MBCS/UTF-8 needs no conversion
2978 if (a_uInputDataLen > a_uOutputDataSize) {
2979 return false;
2980 }
2981 memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
2982 return true;
2983 }
2984
2996 const SI_CHAR * a_pInputData)
2997 {
2998 // ASCII/MBCS/UTF-8 needs no conversion
2999 return strlen((const char *)a_pInputData) + 1;
3000 }
3001
3016 const SI_CHAR * a_pInputData,
3017 char * a_pOutputData,
3018 size_t a_uOutputDataSize)
3019 {
3020 // calc input string length (SI_CHAR type and size independent)
3021 size_t uInputLen = strlen((const char *)a_pInputData) + 1;
3022 if (uInputLen > a_uOutputDataSize) {
3023 return false;
3024 }
3025
3026 // ascii/UTF-8 needs no conversion
3027 memcpy(a_pOutputData, a_pInputData, uInputLen);
3028 return true;
3029 }
3030};
3031
3032
3033// ---------------------------------------------------------------------------
3034// SI_CONVERT_GENERIC
3035// ---------------------------------------------------------------------------
3036#ifdef SI_CONVERT_GENERIC
3037
3038#define SI_Case SI_GenericCase
3039#define SI_NoCase SI_GenericNoCase
3040
3041#include <wchar.h>
3042#include "ConvertUTF.h"
3043
3048template<class SI_CHAR>
3049class SI_ConvertW {
3050 bool m_bStoreIsUtf8;
3051protected:
3052 SI_ConvertW() { }
3053public:
3054 SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
3055
3056 /* copy and assignment */
3057 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
3058 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
3059 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
3060 return *this;
3061 }
3062
3076 size_t SizeFromStore(
3077 const char * a_pInputData,
3078 size_t a_uInputDataLen)
3079 {
3080 SI_ASSERT(a_uInputDataLen != (size_t) -1);
3081
3082 if (m_bStoreIsUtf8) {
3083 // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
3084 // so we just return the same number of characters required as for
3085 // the source text.
3086 return a_uInputDataLen;
3087 }
3088
3089 // get the required buffer size
3090#if defined(_MSC_VER)
3091 size_t uBufSiz;
3092 errno_t e = mbstowcs_s(&uBufSiz, NULL, 0, a_pInputData, a_uInputDataLen);
3093 return (e == 0) ? uBufSiz : (size_t) -1;
3094#elif !defined(SI_NO_MBSTOWCS_NULL)
3095 return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
3096#else
3097 // fall back processing for platforms that don't support a NULL dest to mbstowcs
3098 // worst case scenario is 1:1, this will be a sufficient buffer size
3099 (void)a_pInputData;
3100 return a_uInputDataLen;
3101#endif
3102 }
3103
3117 bool ConvertFromStore(
3118 const char * a_pInputData,
3119 size_t a_uInputDataLen,
3120 SI_CHAR * a_pOutputData,
3121 size_t a_uOutputDataSize)
3122 {
3123 if (m_bStoreIsUtf8) {
3124 // This uses the Unicode reference implementation to do the
3125 // conversion from UTF-8 to wchar_t. The required files are
3126 // ConvertUTF.h and ConvertUTF.c which should be included in
3127 // the distribution but are publicly available from unicode.org
3128 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
3129 ConversionResult retval;
3130 const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
3131 if (sizeof(wchar_t) == sizeof(UTF32)) {
3132 UTF32 * pUtf32 = (UTF32 *) a_pOutputData;
3133 retval = ConvertUTF8toUTF32(
3134 &pUtf8, pUtf8 + a_uInputDataLen,
3135 &pUtf32, pUtf32 + a_uOutputDataSize,
3136 lenientConversion);
3137 }
3138 else if (sizeof(wchar_t) == sizeof(UTF16)) {
3139 UTF16 * pUtf16 = (UTF16 *) a_pOutputData;
3140 retval = ConvertUTF8toUTF16(
3141 &pUtf8, pUtf8 + a_uInputDataLen,
3142 &pUtf16, pUtf16 + a_uOutputDataSize,
3143 lenientConversion);
3144 }
3145 return retval == conversionOK;
3146 }
3147
3148 // convert to wchar_t
3149#if defined(_MSC_VER)
3150 size_t uBufSiz;
3151 errno_t e = mbstowcs_s(&uBufSiz,
3152 a_pOutputData, a_uOutputDataSize,
3153 a_pInputData, a_uInputDataLen);
3154 (void)uBufSiz;
3155 return (e == 0);
3156#else
3157 size_t retval = mbstowcs(a_pOutputData,
3158 a_pInputData, a_uOutputDataSize);
3159 return retval != (size_t)(-1);
3160#endif
3161 }
3162
3173 size_t SizeToStore(
3174 const SI_CHAR * a_pInputData)
3175 {
3176 if (m_bStoreIsUtf8) {
3177 // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
3178 size_t uLen = 0;
3179 while (a_pInputData[uLen]) {
3180 ++uLen;
3181 }
3182 return (6 * uLen) + 1;
3183 }
3184 else {
3185 size_t uLen = wcstombs(NULL, a_pInputData, 0);
3186 if (uLen == (size_t)(-1)) {
3187 return uLen;
3188 }
3189 return uLen + 1; // include NULL terminator
3190 }
3191 }
3192
3206 bool ConvertToStore(
3207 const SI_CHAR * a_pInputData,
3208 char * a_pOutputData,
3209 size_t a_uOutputDataSize
3210 )
3211 {
3212 if (m_bStoreIsUtf8) {
3213 // calc input string length (SI_CHAR type and size independent)
3214 size_t uInputLen = 0;
3215 while (a_pInputData[uInputLen]) {
3216 ++uInputLen;
3217 }
3218 ++uInputLen; // include the NULL char
3219
3220 // This uses the Unicode reference implementation to do the
3221 // conversion from wchar_t to UTF-8. The required files are
3222 // ConvertUTF.h and ConvertUTF.c which should be included in
3223 // the distribution but are publicly available from unicode.org
3224 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
3225 ConversionResult retval;
3226 UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
3227 if (sizeof(wchar_t) == sizeof(UTF32)) {
3228 const UTF32 * pUtf32 = (const UTF32 *) a_pInputData;
3229 retval = ConvertUTF32toUTF8(
3230 &pUtf32, pUtf32 + uInputLen,
3231 &pUtf8, pUtf8 + a_uOutputDataSize,
3232 lenientConversion);
3233 }
3234 else if (sizeof(wchar_t) == sizeof(UTF16)) {
3235 const UTF16 * pUtf16 = (const UTF16 *) a_pInputData;
3236 retval = ConvertUTF16toUTF8(
3237 &pUtf16, pUtf16 + uInputLen,
3238 &pUtf8, pUtf8 + a_uOutputDataSize,
3239 lenientConversion);
3240 }
3241 return retval == conversionOK;
3242 }
3243 else {
3244 size_t retval = wcstombs(a_pOutputData,
3245 a_pInputData, a_uOutputDataSize);
3246 return retval != (size_t) -1;
3247 }
3248 }
3249};
3250
3251#endif // SI_CONVERT_GENERIC
3252
3253
3254// ---------------------------------------------------------------------------
3255// SI_CONVERT_ICU
3256// ---------------------------------------------------------------------------
3257#ifdef SI_CONVERT_ICU
3258
3259#define SI_Case SI_GenericCase
3260#define SI_NoCase SI_GenericNoCase
3261
3262#include <unicode/ucnv.h>
3263
3267template<class SI_CHAR>
3268class SI_ConvertW {
3269 const char * m_pEncoding;
3270 UConverter * m_pConverter;
3271protected:
3272 SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
3273public:
3274 SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
3275 m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
3276 }
3277
3278 /* copy and assignment */
3279 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
3280 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
3281 m_pEncoding = rhs.m_pEncoding;
3282 m_pConverter = NULL;
3283 return *this;
3284 }
3285 ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); }
3286
3300 size_t SizeFromStore(
3301 const char * a_pInputData,
3302 size_t a_uInputDataLen)
3303 {
3304 SI_ASSERT(a_uInputDataLen != (size_t) -1);
3305
3306 UErrorCode nError;
3307
3308 if (!m_pConverter) {
3309 nError = U_ZERO_ERROR;
3310 m_pConverter = ucnv_open(m_pEncoding, &nError);
3311 if (U_FAILURE(nError)) {
3312 return (size_t) -1;
3313 }
3314 }
3315
3316 nError = U_ZERO_ERROR;
3317 int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,
3318 a_pInputData, (int32_t) a_uInputDataLen, &nError);
3319 if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3320 return (size_t) -1;
3321 }
3322
3323 return (size_t) nLen;
3324 }
3325
3339 bool ConvertFromStore(
3340 const char * a_pInputData,
3341 size_t a_uInputDataLen,
3342 UChar * a_pOutputData,
3343 size_t a_uOutputDataSize)
3344 {
3345 UErrorCode nError;
3346
3347 if (!m_pConverter) {
3348 nError = U_ZERO_ERROR;
3349 m_pConverter = ucnv_open(m_pEncoding, &nError);
3350 if (U_FAILURE(nError)) {
3351 return false;
3352 }
3353 }
3354
3355 nError = U_ZERO_ERROR;
3356 ucnv_toUChars(m_pConverter,
3357 a_pOutputData, (int32_t) a_uOutputDataSize,
3358 a_pInputData, (int32_t) a_uInputDataLen, &nError);
3359 if (U_FAILURE(nError)) {
3360 return false;
3361 }
3362
3363 return true;
3364 }
3365
3376 size_t SizeToStore(
3377 const UChar * a_pInputData)
3378 {
3379 UErrorCode nError;
3380
3381 if (!m_pConverter) {
3382 nError = U_ZERO_ERROR;
3383 m_pConverter = ucnv_open(m_pEncoding, &nError);
3384 if (U_FAILURE(nError)) {
3385 return (size_t) -1;
3386 }
3387 }
3388
3389 nError = U_ZERO_ERROR;
3390 int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,
3391 a_pInputData, -1, &nError);
3392 if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3393 return (size_t) -1;
3394 }
3395
3396 return (size_t) nLen + 1;
3397 }
3398
3412 bool ConvertToStore(
3413 const UChar * a_pInputData,
3414 char * a_pOutputData,
3415 size_t a_uOutputDataSize)
3416 {
3417 UErrorCode nError;
3418
3419 if (!m_pConverter) {
3420 nError = U_ZERO_ERROR;
3421 m_pConverter = ucnv_open(m_pEncoding, &nError);
3422 if (U_FAILURE(nError)) {
3423 return false;
3424 }
3425 }
3426
3427 nError = U_ZERO_ERROR;
3428 ucnv_fromUChars(m_pConverter,
3429 a_pOutputData, (int32_t) a_uOutputDataSize,
3430 a_pInputData, -1, &nError);
3431 if (U_FAILURE(nError)) {
3432 return false;
3433 }
3434
3435 return true;
3436 }
3437};
3438
3439#endif // SI_CONVERT_ICU
3440
3441
3442// ---------------------------------------------------------------------------
3443// SI_CONVERT_WIN32
3444// ---------------------------------------------------------------------------
3445#ifdef SI_CONVERT_WIN32
3446
3447#define SI_Case SI_GenericCase
3448
3449// Windows CE doesn't have errno or MBCS libraries
3450#ifdef _WIN32_WCE
3451# ifndef SI_NO_MBCS
3452# define SI_NO_MBCS
3453# endif
3454#endif
3455
3456#include <windows.h>
3457#ifdef SI_NO_MBCS
3458# define SI_NoCase SI_GenericNoCase
3459#else // !SI_NO_MBCS
3468#include <mbstring.h>
3469template<class SI_CHAR>
3470struct SI_NoCase {
3471 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
3472 if (sizeof(SI_CHAR) == sizeof(char)) {
3473 return _mbsicmp((const unsigned char *)pLeft,
3474 (const unsigned char *)pRight) < 0;
3475 }
3476 if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
3477 return _wcsicmp((const wchar_t *)pLeft,
3478 (const wchar_t *)pRight) < 0;
3479 }
3481 }
3482};
3483#endif // SI_NO_MBCS
3484
3491template<class SI_CHAR>
3492class SI_ConvertW {
3494protected:
3495 SI_ConvertW() { }
3496public:
3499 }
3500
3501 /* copy and assignment */
3502 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
3503 SI_ConvertW & operator=(const SI_ConvertW & rhs) {
3504 m_uCodePage = rhs.m_uCodePage;
3505 return *this;
3506 }
3507
3521 size_t SizeFromStore(
3522 const char * a_pInputData,
3523 size_t a_uInputDataLen)
3524 {
3525 SI_ASSERT(a_uInputDataLen != (size_t) -1);
3526
3528 m_uCodePage, 0,
3530 0, 0);
3531 return (size_t)(retval > 0 ? retval : -1);
3532 }
3533
3547 bool ConvertFromStore(
3548 const char * a_pInputData,
3549 size_t a_uInputDataLen,
3551 size_t a_uOutputDataSize)
3552 {
3554 m_uCodePage, 0,
3556 (wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
3557 return (nSize > 0);
3558 }
3559
3570 size_t SizeToStore(
3571 const SI_CHAR * a_pInputData)
3572 {
3574 m_uCodePage, 0,
3575 (const wchar_t *) a_pInputData, -1,
3576 0, 0, 0, 0);
3577 return (size_t) (retval > 0 ? retval : -1);
3578 }
3579
3593 bool ConvertToStore(
3594 const SI_CHAR * a_pInputData,
3595 char * a_pOutputData,
3596 size_t a_uOutputDataSize)
3597 {
3599 m_uCodePage, 0,
3600 (const wchar_t *) a_pInputData, -1,
3601 a_pOutputData, (int) a_uOutputDataSize, 0, 0);
3602 return retval > 0;
3603 }
3604};
3605
3606#endif // SI_CONVERT_WIN32
3607
3608
3609
3610// ---------------------------------------------------------------------------
3611// SI_NO_CONVERSION
3612// ---------------------------------------------------------------------------
3613#ifdef SI_NO_CONVERSION
3614
3615#define SI_Case SI_GenericCase
3616#define SI_NoCase SI_GenericNoCase
3617
3618#endif // SI_NO_CONVERSION
3619
3620
3621
3622// ---------------------------------------------------------------------------
3623// TYPE DEFINITIONS
3624// ---------------------------------------------------------------------------
3625
3626typedef CSimpleIniTempl<char,
3628typedef CSimpleIniTempl<char,
3630
3631#if defined(SI_NO_CONVERSION)
3632// if there is no wide char conversion then we don't need to define the
3633// widechar "W" versions of CSimpleIni
3634# define CSimpleIni CSimpleIniA
3635# define CSimpleIniCase CSimpleIniCaseA
3636# define SI_NEWLINE SI_NEWLINE_A
3637#else
3638# if defined(SI_CONVERT_ICU)
3639typedef CSimpleIniTempl<UChar,
3641typedef CSimpleIniTempl<UChar,
3643# else
3644typedef CSimpleIniTempl<wchar_t,
3646typedef CSimpleIniTempl<wchar_t,
3648# endif
3649
3650# ifdef _UNICODE
3651# define CSimpleIni CSimpleIniW
3652# define CSimpleIniCase CSimpleIniCaseW
3653# define SI_NEWLINE SI_NEWLINE_W
3654# else // !_UNICODE
3655# define CSimpleIni CSimpleIniA
3656# define CSimpleIniCase CSimpleIniCaseA
3657# define SI_NEWLINE SI_NEWLINE_A
3658# endif // _UNICODE
3659#endif
3660
3661#ifdef _MSC_VER
3662# pragma warning (pop)
3663#endif
3664
3665#endif // INCLUDED_SimpleIni_h
3666
Definition SimpleIni.h:440
Definition SimpleIni.h:397
Definition SimpleIni.h:386
Definition SimpleIni.h:410
Definition SimpleIni.h:319
bool DeleteValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pValue, bool a_bRemoveEmpty=false)
Definition SimpleIni.h:2756
bool GetAllKeys(const SI_CHAR *a_pSection, TNamesDepend &a_names) const
Definition SimpleIni.h:2479
int GetSectionSize(const SI_CHAR *a_pSection) const
Definition SimpleIni.h:2416
void SetAllowKeyOnly(bool a_bAllowKeyOnly=true)
Definition SimpleIni.h:588
SI_Error LoadData(const std::string &a_strData)
Definition SimpleIni.h:654
bool UsingQuotes() const
Definition SimpleIni.h:578
std::map< Entry, TKeyVal, typename Entry::KeyOrder > TSection
Definition SimpleIni.h:376
std::list< Entry > TNamesDepend
Definition SimpleIni.h:381
bool IsUnicode() const
Definition SimpleIni.h:514
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:2355
bool IsMultiKey() const
Definition SimpleIni.h:539
Converter GetConverter() const
Definition SimpleIni.h:1191
bool IsEmpty() const
Definition SimpleIni.h:490
const TKeyVal * GetSection(const SI_CHAR *a_pSection) const
Definition SimpleIni.h:2451
SI_Error Save(OutputWriter &a_oOutput, bool a_bAddSignature=false) const
Definition SimpleIni.h:2567
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:2149
bool GetBoolValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bDefault=false, bool *a_pHasMultiple=NULL) const
Definition SimpleIni.h:2320
long GetLongValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, long a_nDefault=0, bool *a_pHasMultiple=NULL) const
Definition SimpleIni.h:2186
void GetAllSections(TNamesDepend &a_names) const
Definition SimpleIni.h:2466
void Reset()
Definition SimpleIni.h:1382
~CSimpleIniTempl()
Definition SimpleIni.h:1375
bool UsingSpaces() const
Definition SimpleIni.h:566
SI_Error Save(std::string &a_sBuffer, bool a_bAddSignature=false) const
Definition SimpleIni.h:794
bool KeyExists(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey) const
Definition SimpleIni.h:907
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:1032
bool SectionExists(const SI_CHAR *a_pSection) const
Definition SimpleIni.h:900
bool Delete(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bRemoveEmpty=false)
Definition SimpleIni.h:2745
void SetMultiLine(bool a_bAllowMultiLine=true)
Definition SimpleIni.h:548
void SetQuotes(bool a_bParseQuotes=true)
Definition SimpleIni.h:573
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:2257
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:2225
void SetMultiKey(bool a_bAllowMultiKey=true)
Definition SimpleIni.h:534
bool GetAllValues(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, TNamesDepend &a_values) const
Definition SimpleIni.h:2381
void SetSpaces(bool a_bSpaces=true)
Definition SimpleIni.h:561
std::multimap< Entry, const SI_CHAR *, typename Entry::KeyOrder > TKeyVal
Definition SimpleIni.h:373
SI_Error LoadFile(const char *a_pszFile)
Definition SimpleIni.h:1405
bool GetAllowKeyOnly() const
Definition SimpleIni.h:593
SI_Error SaveFile(const char *a_pszFile, bool a_bAddSignature=true) const
Definition SimpleIni.h:2510
CSimpleIniTempl(bool a_bIsUtf8=false, bool a_bMultiKey=false, bool a_bMultiLine=false)
Definition SimpleIni.h:1356
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:2289
bool IsMultiLine() const
Definition SimpleIni.h:553
Definition SimpleIni.h:2920
bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData, size_t a_uOutputDataSize)
Definition SimpleIni.h:3015
size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen)
Definition SimpleIni.h:2947
size_t SizeToStore(const SI_CHAR *a_pInputData)
Definition SimpleIni.h:2995
bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen, SI_CHAR *a_pOutputData, size_t a_uOutputDataSize)
Definition SimpleIni.h:2971
Definition SimpleIni.h:354
Definition SimpleIni.h:362
Definition SimpleIni.h:324
Definition SimpleIni.h:2880
Definition SimpleIni.h:2900