SimpleIni
Loading...
Searching...
No Matches
SimpleIni.h
1
226#ifndef INCLUDED_SimpleIni_h
227#define INCLUDED_SimpleIni_h
228
229#if defined(_MSC_VER) && (_MSC_VER >= 1020)
230#pragma once
231#endif
232
233// Disable these warnings in MSVC:
234// 4127 "conditional expression is constant" as the conversion classes trigger
235// it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
236// be optimized away in a release build.
237// 4503 'insert' : decorated name length exceeded, name was truncated
238// 4702 "unreachable code" as the MS STL header causes it in release mode.
239// Again, the code causing the warning will be cleaned up by the compiler.
240// 4786 "identifier truncated to 256 characters" as this is thrown hundreds
241// of times VC6 as soon as STL is used.
242#ifdef _MSC_VER
243#pragma warning(push)
244#pragma warning(disable : 4127 4503 4702 4786)
245#endif
246
247#include <algorithm>
248#include <cerrno>
249#include <cstdlib>
250#include <cstring>
251#include <limits>
252#include <list>
253#include <map>
254#include <stdio.h>
255#include <string>
256
257#ifdef SI_SUPPORT_IOSTREAMS
258#include <iostream>
259#endif // SI_SUPPORT_IOSTREAMS
260
261#ifdef _DEBUG
262#ifndef assert
263#include <cassert>
264#endif
265#define SI_ASSERT(x) assert(x)
266#else
267#define SI_ASSERT(x)
268#endif
269
270using SI_Error = int;
271
272constexpr int SI_OK = 0;
273constexpr int SI_UPDATED = 1;
274constexpr int SI_INSERTED = 2;
275
276// note: test for any error with (retval < 0)
277constexpr int SI_FAIL = -1;
278constexpr int SI_NOMEM = -2;
279constexpr int SI_FILE = -3;
280
283constexpr size_t SI_MAX_FILE_SIZE = 1024ULL * 1024ULL * 1024ULL;
284
285#define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
286
287#ifdef _WIN32
288#define SI_NEWLINE_A "\r\n"
289#define SI_NEWLINE_W L"\r\n"
290#else // !_WIN32
291#define SI_NEWLINE_A "\n"
292#define SI_NEWLINE_W L"\n"
293#endif // _WIN32
294
295#if defined(SI_CONVERT_ICU)
296#include <unicode/ustring.h>
297#endif
298
299#if defined(_WIN32)
300#define SI_HAS_WIDE_FILE
301#define SI_WCHAR_T wchar_t
302#elif defined(SI_CONVERT_ICU)
303#define SI_HAS_WIDE_FILE
304#define SI_WCHAR_T UChar
305#endif
306
307// ---------------------------------------------------------------------------
308// MAIN TEMPLATE CLASS
309// ---------------------------------------------------------------------------
310
330template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
332public:
333 typedef SI_CHAR SI_CHAR_T;
334
336 struct Entry {
337 const SI_CHAR *pItem;
338 const SI_CHAR *pComment;
339 int nOrder;
340
341 Entry(const SI_CHAR *a_pszItem = NULL, int a_nOrder = 0)
342 : pItem(a_pszItem), pComment(NULL), nOrder(a_nOrder) {}
343 Entry(const SI_CHAR *a_pszItem, const SI_CHAR *a_pszComment, int a_nOrder)
344 : pItem(a_pszItem), pComment(a_pszComment), nOrder(a_nOrder) {}
345 Entry(const Entry &rhs) { operator=(rhs); }
346 Entry &operator=(const Entry &rhs) {
347 pItem = rhs.pItem;
348 pComment = rhs.pComment;
349 nOrder = rhs.nOrder;
350 return *this;
351 }
352
353#if defined(_MSC_VER) && _MSC_VER <= 1200
355 bool operator<(const Entry &rhs) const { return LoadOrder()(*this, rhs); }
356 bool operator>(const Entry &rhs) const { return LoadOrder()(rhs, *this); }
357#endif
358
360 struct KeyOrder {
361 bool operator()(const Entry &lhs, const Entry &rhs) const {
362 const static SI_STRLESS isLess = SI_STRLESS();
363 return isLess(lhs.pItem, rhs.pItem);
364 }
365 };
366
368 struct LoadOrder {
369 bool operator()(const Entry &lhs, const Entry &rhs) const {
370 if (lhs.nOrder != rhs.nOrder) {
371 return lhs.nOrder < rhs.nOrder;
372 }
373 return KeyOrder()(lhs, rhs);
374 }
375 };
376 };
377
379 typedef std::multimap<Entry, const SI_CHAR *, typename Entry::KeyOrder>
381
383 typedef std::map<Entry, TKeyVal, typename Entry::KeyOrder> TSection;
384
388 typedef std::list<Entry> TNamesDepend;
389
394 public:
395 OutputWriter() {}
396 virtual ~OutputWriter() {}
397 virtual void Write(const char *a_pBuf) = 0;
398
399 private:
400 OutputWriter(const OutputWriter &); // disable
401 OutputWriter &operator=(const OutputWriter &); // disable
402 };
403
405 class FileWriter : public OutputWriter {
406 FILE *m_file;
407
408 public:
409 FileWriter(FILE *a_file) : m_file(a_file) {}
410 void Write(const char *a_pBuf) { fputs(a_pBuf, m_file); }
411
412 private:
413 FileWriter(const FileWriter &); // disable
414 FileWriter &operator=(const FileWriter &); // disable
415 };
416
418 class StringWriter : public OutputWriter {
419 std::string &m_string;
420
421 public:
422 StringWriter(std::string &a_string) : m_string(a_string) {}
423 void Write(const char *a_pBuf) { m_string.append(a_pBuf); }
424
425 private:
426 StringWriter(const StringWriter &); // disable
427 StringWriter &operator=(const StringWriter &); // disable
428 };
429
430#ifdef SI_SUPPORT_IOSTREAMS
432 class StreamWriter : public OutputWriter {
433 std::ostream &m_ostream;
434
435 public:
436 StreamWriter(std::ostream &a_ostream) : m_ostream(a_ostream) {}
437 void Write(const char *a_pBuf) { m_ostream << a_pBuf; }
438
439 private:
440 StreamWriter(const StreamWriter &); // disable
441 StreamWriter &operator=(const StreamWriter &); // disable
442 };
443#endif // SI_SUPPORT_IOSTREAMS
444
448 class Converter : private SI_CONVERTER {
449 public:
450 Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) {
451 m_scratch.resize(1024);
452 }
453 Converter(const Converter &rhs)
454 : SI_CONVERTER(rhs), m_scratch(rhs.m_scratch) {}
455 Converter &operator=(const Converter &rhs) {
456 SI_CONVERTER::operator=(rhs);
457 m_scratch = rhs.m_scratch;
458 return *this;
459 }
460 bool ConvertToStore(const SI_CHAR *a_pszString) {
461 size_t uLen = SI_CONVERTER::SizeToStore(a_pszString);
462 if (uLen == (size_t)(-1)) {
463 return false;
464 }
465 while (uLen > m_scratch.size()) {
466 m_scratch.resize(m_scratch.size() * 2);
467 }
468 return SI_CONVERTER::ConvertToStore(
469 a_pszString, const_cast<char *>(m_scratch.data()), m_scratch.size());
470 }
471 const char *Data() { return m_scratch.data(); }
472
473 private:
474 std::string m_scratch;
475 };
476
477public:
478 /*-----------------------------------------------------------------------*/
479
486 CSimpleIniTempl(bool a_bIsUtf8 = false, bool a_bMultiKey = false,
487 bool a_bMultiLine = false);
488
491
493 void Reset();
494
496 bool IsEmpty() const { return m_data.empty(); }
497
498 /*-----------------------------------------------------------------------*/
515 void SetUnicode(bool a_bIsUtf8 = true) {
516 if (!m_pData)
517 m_bStoreIsUtf8 = a_bIsUtf8;
518 }
519
521 bool IsUnicode() const { return m_bStoreIsUtf8; }
522
541 void SetMultiKey(bool a_bAllowMultiKey = true) {
542 m_bAllowMultiKey = a_bAllowMultiKey;
543 }
544
546 bool IsMultiKey() const { return m_bAllowMultiKey; }
547
555 void SetMultiLine(bool a_bAllowMultiLine = true) {
556 m_bAllowMultiLine = a_bAllowMultiLine;
557 }
558
560 bool IsMultiLine() const { return m_bAllowMultiLine; }
561
568 void SetSpaces(bool a_bSpaces = true) { m_bSpaces = a_bSpaces; }
569
571 bool UsingSpaces() const { return m_bSpaces; }
572
577 void SetQuotes(bool a_bParseQuotes = true) {
578 m_bParseQuotes = a_bParseQuotes;
579 }
580
582 bool UsingQuotes() const { return m_bParseQuotes; }
583
592 void SetAllowKeyOnly(bool a_bAllowKeyOnly = true) {
593 m_bAllowKeyOnly = a_bAllowKeyOnly;
594 }
595
597 bool GetAllowKeyOnly() const { return m_bAllowKeyOnly; }
598
599 /*-----------------------------------------------------------------------*/
611 SI_Error LoadFile(const char *a_pszFile);
612
613#ifdef SI_HAS_WIDE_FILE
620 SI_Error LoadFile(const SI_WCHAR_T *a_pwszFile);
621#endif // SI_HAS_WIDE_FILE
622
630 SI_Error LoadFile(FILE *a_fpFile);
631
632#ifdef SI_SUPPORT_IOSTREAMS
643 SI_Error LoadData(std::istream &a_istream);
644#endif // SI_SUPPORT_IOSTREAMS
645
652 SI_Error LoadData(const std::string &a_strData) {
653 return LoadData(a_strData.c_str(), a_strData.size());
654 }
655
663 SI_Error LoadData(const char *a_pData, size_t a_uDataLen);
664
665 /*-----------------------------------------------------------------------*/
681 SI_Error SaveFile(const char *a_pszFile, bool a_bAddSignature = true) const;
682
683#ifdef SI_HAS_WIDE_FILE
694 SI_Error SaveFile(const SI_WCHAR_T *a_pwszFile,
695 bool a_bAddSignature = true) const;
696#endif // _WIN32
697
710 SI_Error SaveFile(FILE *a_pFile, bool a_bAddSignature = false) const;
711
743 SI_Error Save(OutputWriter &a_oOutput, bool a_bAddSignature = false) const;
744
745#ifdef SI_SUPPORT_IOSTREAMS
757 SI_Error Save(std::ostream &a_ostream, bool a_bAddSignature = false) const {
758 StreamWriter writer(a_ostream);
759 return Save(writer, a_bAddSignature);
760 }
761#endif // SI_SUPPORT_IOSTREAMS
762
774 SI_Error Save(std::string &a_sBuffer, bool a_bAddSignature = false) const {
775 StringWriter writer(a_sBuffer);
776 return Save(writer, a_bAddSignature);
777 }
778
779 /*-----------------------------------------------------------------------*/
797 void GetAllSections(TNamesDepend &a_names) const;
798
816 bool GetAllKeys(const SI_CHAR *a_pSection, TNamesDepend &a_names) const;
817
834 bool GetAllValues(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
835 TNamesDepend &a_values) const;
836
846 int GetSectionSize(const SI_CHAR *a_pSection) const;
847
861 const TKeyVal *GetSection(const SI_CHAR *a_pSection) const;
862
864 inline bool SectionExists(const SI_CHAR *a_pSection) const {
865 return GetSection(a_pSection) != NULL;
866 }
867
869 inline bool KeyExists(const SI_CHAR *a_pSection,
870 const SI_CHAR *a_pKey) const {
871 return GetValue(a_pSection, a_pKey) != NULL;
872 }
873
891 const SI_CHAR *GetValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
892 const SI_CHAR *a_pDefault = NULL,
893 bool *a_pHasMultiple = NULL) const;
894
908 long GetLongValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
909 long a_nDefault = 0, bool *a_pHasMultiple = NULL) const;
910
924 double GetDoubleValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
925 double a_nDefault = 0,
926 bool *a_pHasMultiple = NULL) const;
927
946 bool GetBoolValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
947 bool a_bDefault = false, bool *a_pHasMultiple = NULL) const;
948
978 SI_Error SetValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
979 const SI_CHAR *a_pValue, const SI_CHAR *a_pComment = NULL,
980 bool a_bForceReplace = false) {
981 if (!a_pSection)
982 return SI_FAIL;
983 return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace,
984 true);
985 }
986
1010 SI_Error SetLongValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
1011 long a_nValue, const SI_CHAR *a_pComment = NULL,
1012 bool a_bUseHex = false, bool a_bForceReplace = false);
1013
1034 SI_Error SetDoubleValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
1035 double a_nValue, const SI_CHAR *a_pComment = NULL,
1036 bool a_bForceReplace = false);
1037
1058 SI_Error SetBoolValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
1059 bool a_bValue, const SI_CHAR *a_pComment = NULL,
1060 bool a_bForceReplace = false);
1061
1080 bool Delete(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
1081 bool a_bRemoveEmpty = false);
1082
1103 bool DeleteValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
1104 const SI_CHAR *a_pValue, bool a_bRemoveEmpty = false);
1105
1106 /*-----------------------------------------------------------------------*/
1115 Converter GetConverter() const { return Converter(m_bStoreIsUtf8); }
1116
1117 /*-----------------------------------------------------------------------*/
1120private:
1121 // copying is not permitted
1122 CSimpleIniTempl(const CSimpleIniTempl &); // disabled
1123 CSimpleIniTempl &operator=(const CSimpleIniTempl &); // disabled
1124
1127 SI_Error FindFileComment(SI_CHAR *&a_pData, bool a_bCopyStrings);
1128
1133 bool FindEntry(SI_CHAR *&a_pData, const SI_CHAR *&a_pSection,
1134 const SI_CHAR *&a_pKey, const SI_CHAR *&a_pVal,
1135 const SI_CHAR *&a_pComment) const;
1136
1159 SI_Error AddEntry(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
1160 const SI_CHAR *a_pValue, const SI_CHAR *a_pComment,
1161 bool a_bForceReplace, bool a_bCopyStrings);
1162
1164 inline bool IsSpace(SI_CHAR ch) const {
1165 return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
1166 }
1167
1169 inline bool IsComment(SI_CHAR ch) const { return (ch == ';' || ch == '#'); }
1170
1172 inline void SkipNewLine(SI_CHAR *&a_pData) const {
1173 a_pData += (*a_pData == '\r' && *(a_pData + 1) == '\n') ? 2 : 1;
1174 }
1175
1177 SI_Error CopyString(const SI_CHAR *&a_pString);
1178
1185 void UndoIncrementalLoadData(const TNamesDepend &a_oAddedSections,
1186 const TNamesDepend &a_oAddedKeys);
1187
1189 void DeleteString(const SI_CHAR *a_pString);
1190
1192 bool IsLess(const SI_CHAR *a_pLeft, const SI_CHAR *a_pRight) const {
1193 const static SI_STRLESS isLess = SI_STRLESS();
1194 return isLess(a_pLeft, a_pRight);
1195 }
1196
1197 bool IsMultiLineTag(const SI_CHAR *a_pData) const;
1198 bool IsMultiLineData(const SI_CHAR *a_pData) const;
1199 bool IsSingleLineQuotedValue(const SI_CHAR *a_pData) const;
1200 bool MultiLineTextHasTag(const SI_CHAR *a_pText, const SI_CHAR *a_pTag) const;
1201 bool LoadMultiLineText(SI_CHAR *&a_pData, const SI_CHAR *&a_pVal,
1202 const SI_CHAR *a_pTagName,
1203 bool a_bAllowBlankLinesInComment = false) const;
1204 bool IsNewLineChar(SI_CHAR a_c) const;
1205
1206 bool OutputMultiLineText(OutputWriter &a_oOutput, Converter &a_oConverter,
1207 const SI_CHAR *a_pText) const;
1208
1209private:
1215 SI_CHAR *m_pData;
1216
1221 size_t m_uDataLen;
1222
1224 const SI_CHAR *m_pFileComment;
1225
1227 const SI_CHAR m_cEmptyString;
1228
1230 TSection m_data;
1231
1236 TNamesDepend m_strings;
1237
1239 bool m_bStoreIsUtf8;
1240
1242 bool m_bAllowMultiKey;
1243
1245 bool m_bAllowMultiLine;
1246
1248 bool m_bSpaces;
1249
1251 bool m_bParseQuotes;
1252
1254 bool m_bAllowKeyOnly;
1255
1259 int m_nOrder;
1260};
1261
1262// ---------------------------------------------------------------------------
1263// IMPLEMENTATION
1264// ---------------------------------------------------------------------------
1265
1266template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1268 bool a_bIsUtf8, bool a_bAllowMultiKey, bool a_bAllowMultiLine)
1269 : m_pData(0), m_uDataLen(0), m_pFileComment(NULL), m_cEmptyString(0),
1270 m_bStoreIsUtf8(a_bIsUtf8), m_bAllowMultiKey(a_bAllowMultiKey),
1271 m_bAllowMultiLine(a_bAllowMultiLine), m_bSpaces(true),
1272 m_bParseQuotes(false), m_bAllowKeyOnly(false), m_nOrder(0) {}
1273
1274template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1278
1279template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1281 // remove all data
1282 delete[] m_pData;
1283 m_pData = NULL;
1284 m_uDataLen = 0;
1285 m_pFileComment = NULL;
1286 m_nOrder = 0;
1287 if (!m_data.empty()) {
1288 m_data.erase(m_data.begin(), m_data.end());
1289 }
1290
1291 // remove all strings
1292 if (!m_strings.empty()) {
1293 typename TNamesDepend::iterator i = m_strings.begin();
1294 for (; i != m_strings.end(); ++i) {
1295 delete[] const_cast<SI_CHAR *>(i->pItem);
1296 }
1297 m_strings.erase(m_strings.begin(), m_strings.end());
1298 }
1299}
1300
1301namespace SI_Internal {
1302#ifdef SI_CONVERT_ICU
1304inline SI_Error UCharPathToUtf8(const UChar *a_pPath, char *&a_pUtf8Path) {
1305 a_pUtf8Path = NULL;
1306 UErrorCode status = U_ZERO_ERROR;
1307 int32_t uLen = 0;
1308 u_strToUTF8(NULL, 0, &uLen, a_pPath, -1, &status);
1309 if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
1310 return SI_FILE;
1311 }
1312 if (uLen < 0) {
1313 return SI_FILE;
1314 }
1315 char *pBuf = new (std::nothrow) char[static_cast<size_t>(uLen) + 1];
1316 if (!pBuf) {
1317 return SI_NOMEM;
1318 }
1319 status = U_ZERO_ERROR;
1320 u_strToUTF8(pBuf, uLen + 1, NULL, a_pPath, -1, &status);
1321 if (U_FAILURE(status)) {
1322 delete[] pBuf;
1323 return SI_FILE;
1324 }
1325 a_pUtf8Path = pBuf;
1326 return SI_OK;
1327}
1328#endif // SI_CONVERT_ICU
1329
1331inline bool GetFileSize(FILE *a_fpFile, size_t &a_uSize) {
1332 a_uSize = 0;
1333
1334#if defined(_WIN32) && !defined(_WIN32_WCE)
1335 if (_fseeki64(a_fpFile, 0, SEEK_END) != 0) {
1336 return false;
1337 }
1338 const __int64 nSize = _ftelli64(a_fpFile);
1339 if (nSize < 0) {
1340 return false;
1341 }
1342 if (static_cast<unsigned long long>(nSize) >
1343 static_cast<unsigned long long>(SI_MAX_FILE_SIZE)) {
1344 return false;
1345 }
1346 if (static_cast<unsigned long long>(nSize) >
1347 static_cast<unsigned long long>(std::numeric_limits<size_t>::max())) {
1348 return false;
1349 }
1350 a_uSize = static_cast<size_t>(nSize);
1351 if (_fseeki64(a_fpFile, 0, SEEK_SET) != 0) {
1352 return false;
1353 }
1354 return true;
1355#elif defined(_POSIX_VERSION) || defined(__unix__) || defined(__APPLE__)
1356 if (fseeko(a_fpFile, 0, SEEK_END) != 0) {
1357 return false;
1358 }
1359 const off_t nSize = ftello(a_fpFile);
1360 if (nSize < 0) {
1361 return false;
1362 }
1363 if (static_cast<unsigned long long>(nSize) >
1364 static_cast<unsigned long long>(SI_MAX_FILE_SIZE)) {
1365 return false;
1366 }
1367 if (static_cast<unsigned long long>(nSize) >
1368 static_cast<unsigned long long>(std::numeric_limits<size_t>::max())) {
1369 return false;
1370 }
1371 a_uSize = static_cast<size_t>(nSize);
1372 if (fseeko(a_fpFile, 0, SEEK_SET) != 0) {
1373 return false;
1374 }
1375 return true;
1376#else
1377 if (fseek(a_fpFile, 0, SEEK_END) != 0) {
1378 return false;
1379 }
1380 const long nSize = ftell(a_fpFile);
1381 if (nSize < 0) {
1382 return false;
1383 }
1384 if (static_cast<unsigned long long>(nSize) >
1385 static_cast<unsigned long long>(SI_MAX_FILE_SIZE)) {
1386 return false;
1387 }
1388 a_uSize = static_cast<size_t>(nSize);
1389 if (fseek(a_fpFile, 0, SEEK_SET) != 0) {
1390 return false;
1391 }
1392 return true;
1393#endif
1394}
1395} // namespace SI_Internal
1396
1397template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1399 const char *a_pszFile) {
1400 FILE *fp = NULL;
1401#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1402 fopen_s(&fp, a_pszFile, "rb");
1403#else // !__STDC_WANT_SECURE_LIB__
1404 fp = fopen(a_pszFile, "rb");
1405#endif // __STDC_WANT_SECURE_LIB__
1406 if (!fp) {
1407 return SI_FILE;
1408 }
1409 SI_Error rc = LoadFile(fp);
1410 fclose(fp);
1411 return rc;
1412}
1413
1414#ifdef SI_HAS_WIDE_FILE
1415template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1417 const SI_WCHAR_T *a_pwszFile) {
1418#ifdef _WIN32
1419 FILE *fp = NULL;
1420#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1421 _wfopen_s(&fp, a_pwszFile, L"rb");
1422#else // !__STDC_WANT_SECURE_LIB__
1423 fp = _wfopen(a_pwszFile, L"rb");
1424#endif // __STDC_WANT_SECURE_LIB__
1425 if (!fp)
1426 return SI_FILE;
1427 SI_Error rc = LoadFile(fp);
1428 fclose(fp);
1429 return rc;
1430#else // !_WIN32 (therefore SI_CONVERT_ICU)
1431 char *szFile = NULL;
1432 SI_Error rcPath = SI_Internal::UCharPathToUtf8(a_pwszFile, szFile);
1433 if (rcPath < 0) {
1434 return rcPath;
1435 }
1436 SI_Error rc = LoadFile(szFile);
1437 delete[] szFile;
1438 return rc;
1439#endif // _WIN32
1440}
1441#endif // SI_HAS_WIDE_FILE
1442
1443template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1444SI_Error
1446 size_t uSize = 0;
1447 if (!SI_Internal::GetFileSize(a_fpFile, uSize)) {
1448 return SI_FILE;
1449 }
1450 if (uSize == 0) {
1451 return SI_OK;
1452 }
1453
1454 // allocate and ensure NULL terminated
1455 char *pData = new (std::nothrow) char[uSize + 1];
1456 if (!pData) {
1457 return SI_NOMEM;
1458 }
1459 pData[uSize] = 0;
1460
1461 // load data into buffer
1462 size_t uRead = fread(pData, sizeof(char), uSize, a_fpFile);
1463 if (uRead != uSize) {
1464 delete[] pData;
1465 return SI_FILE;
1466 }
1467
1468 // convert the raw data to unicode
1469 SI_Error rc = LoadData(pData, uRead);
1470 delete[] pData;
1471 return rc;
1472}
1473
1474template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1476 const char *a_pData, size_t a_uDataLen) {
1477 if (!a_pData) {
1478 return SI_OK;
1479 }
1480
1481 // if the UTF-8 BOM exists, consume it and set mode to unicode, if we have
1482 // already loaded data and try to change mode half-way through then this will
1483 // be ignored and we will assert in debug versions
1484 if (a_uDataLen >= 3 && memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
1485 a_pData += 3;
1486 a_uDataLen -= 3;
1487 SI_ASSERT(m_bStoreIsUtf8 || !m_pData); // we don't expect mixed mode data
1488 SetUnicode();
1489 }
1490
1491 if (a_uDataLen == 0) {
1492 return SI_OK;
1493 }
1494
1495 // determine the length of the converted data
1496 SI_CONVERTER converter(m_bStoreIsUtf8);
1497 size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
1498 if (uLen == (size_t)(-1)) {
1499 return SI_FAIL;
1500 }
1501
1502 // check converted data size is within supported limits (SI_MAX_FILE_SIZE)
1503 if (uLen >= (SI_MAX_FILE_SIZE / sizeof(SI_CHAR))) {
1504 return SI_FILE;
1505 }
1506
1507 // allocate memory for the data, ensure that there is a NULL
1508 // terminator wherever the converted data ends
1509 SI_CHAR *pData = new (std::nothrow) SI_CHAR[uLen + 1];
1510 if (!pData) {
1511 return SI_NOMEM;
1512 }
1513 memset(pData, 0, sizeof(SI_CHAR) * (uLen + 1));
1514
1515 // convert the data
1516 if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
1517 delete[] pData;
1518 return SI_FAIL;
1519 }
1520
1521 // parse it
1522 const static SI_CHAR empty = 0;
1523 SI_CHAR *pWork = pData;
1524 const SI_CHAR *pSection = &empty;
1525 const SI_CHAR *pItem = NULL;
1526 const SI_CHAR *pVal = NULL;
1527 const SI_CHAR *pComment = NULL;
1528
1529 // We copy the strings if we are loading data into this class when we
1530 // already have stored some.
1531 bool bCopyStrings = (m_pData != NULL);
1532
1533 size_t nStringsBefore = 0;
1534 const SI_CHAR *pFileCommentBefore = NULL;
1535 TNamesDepend oAddedSections;
1536 TNamesDepend oAddedKeys;
1537 if (bCopyStrings) {
1538 nStringsBefore = m_strings.size();
1539 pFileCommentBefore = m_pFileComment;
1540 }
1541
1542 // find a file comment if it exists, this is a comment that starts at the
1543 // beginning of the file and continues until the first blank line.
1544 SI_Error rc = FindFileComment(pWork, bCopyStrings);
1545 if (rc < 0) {
1546 delete[] pData;
1547 if (bCopyStrings) {
1548 m_pFileComment = pFileCommentBefore;
1549 while (m_strings.size() > nStringsBefore) {
1550 delete[] const_cast<SI_CHAR *>(m_strings.back().pItem);
1551 m_strings.pop_back();
1552 }
1553 } else {
1554 m_pFileComment = NULL;
1555 }
1556 return rc;
1557 }
1558
1559 // add every entry in the file to the data table
1560 while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
1561 bool bSectionExisted = SectionExists(pSection);
1562 bool bKeyExisted = pItem && bSectionExisted && KeyExists(pSection, pItem);
1563
1564 rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings);
1565 if (rc < 0) {
1566 if (bCopyStrings) {
1567 m_pFileComment = pFileCommentBefore;
1568 UndoIncrementalLoadData(oAddedSections, oAddedKeys);
1569 while (m_strings.size() > nStringsBefore) {
1570 delete[] const_cast<SI_CHAR *>(m_strings.back().pItem);
1571 m_strings.pop_back();
1572 }
1573 } else {
1574 m_data.clear();
1575 m_pFileComment = NULL;
1576 m_nOrder = 0;
1577 }
1578 delete[] pData;
1579 return rc;
1580 }
1581
1582 if (bCopyStrings) {
1583 if (!bSectionExisted && *pSection) {
1584 oAddedSections.push_back(Entry(pSection, NULL, 0));
1585 }
1586 if (pItem && !bKeyExisted) {
1587 oAddedKeys.push_back(Entry(pItem, pSection, 0));
1588 }
1589 }
1590 }
1591
1592 // store these strings if we didn't copy them
1593 if (bCopyStrings) {
1594 delete[] pData;
1595 } else {
1596 m_pData = pData;
1597 m_uDataLen = uLen + 1;
1598 }
1599
1600 return SI_OK;
1601}
1602
1603#ifdef SI_SUPPORT_IOSTREAMS
1604template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1606 std::istream &a_istream) {
1607 std::string strData;
1608 char buf[4096];
1609 while (a_istream.read(buf, sizeof(buf)) || a_istream.gcount() > 0) {
1610 const std::streamsize nRead = a_istream.gcount();
1611 for (std::streamsize i = 0; i < nRead; ++i) {
1612 if (buf[i] == '\0') {
1613 return SI_FAIL;
1614 }
1615 }
1616 if (strData.size() + static_cast<size_t>(nRead) > SI_MAX_FILE_SIZE) {
1617 return SI_FILE;
1618 }
1619 strData.append(buf, static_cast<size_t>(nRead));
1620 }
1621 return LoadData(strData.data(), strData.size());
1622}
1623#endif // SI_SUPPORT_IOSTREAMS
1624
1625template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1627 SI_CHAR *&a_pData, bool a_bCopyStrings) {
1628 // there can only be a single file comment
1629 if (m_pFileComment) {
1630 return SI_OK;
1631 }
1632
1633 // Load the file comment as multi-line text, this will modify all of
1634 // the newline characters to be single \n chars
1635 if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
1636 return SI_OK;
1637 }
1638
1639 // copy the string if necessary
1640 if (a_bCopyStrings) {
1641 SI_Error rc = CopyString(m_pFileComment);
1642 if (rc < 0)
1643 return rc;
1644 }
1645
1646 return SI_OK;
1647}
1648
1649template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1651 SI_CHAR *&a_pData, const SI_CHAR *&a_pSection, const SI_CHAR *&a_pKey,
1652 const SI_CHAR *&a_pVal, const SI_CHAR *&a_pComment) const {
1653 a_pComment = NULL;
1654 a_pVal = NULL;
1655
1656 bool bHaveValue = false;
1657 SI_CHAR *pTrail = NULL;
1658 while (*a_pData) {
1659 // skip spaces and empty lines
1660 while (*a_pData && IsSpace(*a_pData)) {
1661 ++a_pData;
1662 }
1663 if (!*a_pData) {
1664 break;
1665 }
1666
1667 // skip processing of comment lines but keep a pointer to
1668 // the start of the comment.
1669 if (IsComment(*a_pData)) {
1670 LoadMultiLineText(a_pData, a_pComment, NULL, true);
1671 continue;
1672 }
1673
1674 // process section names
1675 if (*a_pData == '[') {
1676 // skip leading spaces
1677 ++a_pData;
1678 while (*a_pData && IsSpace(*a_pData)) {
1679 ++a_pData;
1680 }
1681
1682 // find the end of the section name (it may contain spaces)
1683 // and convert it to lowercase as necessary
1684 a_pSection = a_pData;
1685 while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
1686 ++a_pData;
1687 }
1688
1689 // if it's an invalid line, just skip it
1690 if (*a_pData != ']') {
1691 continue;
1692 }
1693
1694 // remove trailing spaces from the section
1695 pTrail = a_pData - 1;
1696 while (pTrail >= a_pSection && IsSpace(*pTrail)) {
1697 --pTrail;
1698 }
1699 ++pTrail;
1700 *pTrail = 0;
1701
1702 // skip to the end of the line
1703 ++a_pData; // safe as checked that it == ']' above
1704 while (*a_pData && !IsNewLineChar(*a_pData)) {
1705 ++a_pData;
1706 }
1707
1708 a_pKey = NULL;
1709 a_pVal = NULL;
1710 return true;
1711 }
1712
1713 // find the end of the key name (it may contain spaces)
1714 a_pKey = a_pData;
1715 while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
1716 ++a_pData;
1717 }
1718 // *a_pData is null, equals, or newline
1719
1720 // if no value and we don't allow no value, then invalid
1721 bHaveValue = (*a_pData == '=');
1722 if (!bHaveValue && !m_bAllowKeyOnly) {
1723 continue;
1724 }
1725
1726 // empty keys are invalid
1727 if (bHaveValue && a_pKey == a_pData) {
1728 while (*a_pData && !IsNewLineChar(*a_pData)) {
1729 ++a_pData;
1730 }
1731 continue;
1732 }
1733
1734 // remove trailing spaces from the key
1735 pTrail = a_pData - 1;
1736 while (pTrail >= a_pKey && IsSpace(*pTrail)) {
1737 --pTrail;
1738 }
1739 ++pTrail;
1740
1741 if (bHaveValue) {
1742 // process the value
1743 *pTrail = 0;
1744
1745 // skip leading whitespace on the value
1746 ++a_pData; // safe as checked that it == '=' above
1747 while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
1748 ++a_pData;
1749 }
1750
1751 // find the end of the value which is the end of this line
1752 a_pVal = a_pData;
1753 while (*a_pData && !IsNewLineChar(*a_pData)) {
1754 ++a_pData;
1755 }
1756
1757 // remove trailing spaces from the value
1758 pTrail = a_pData - 1;
1759 if (*a_pData) { // prepare for the next round
1760 SkipNewLine(a_pData);
1761 }
1762 while (pTrail >= a_pVal && IsSpace(*pTrail)) {
1763 --pTrail;
1764 }
1765 ++pTrail;
1766 *pTrail = 0;
1767
1768 // check for multi-line entries
1769 if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
1770 // skip the "<<<" to get the tag that will end the multiline
1771 const SI_CHAR *pTagName = a_pVal + 3;
1772 return LoadMultiLineText(a_pData, a_pVal, pTagName);
1773 }
1774
1775 // check for quoted values, we are not supporting escapes in quoted values (yet)
1776 if (m_bParseQuotes) {
1777 --pTrail;
1778 if (pTrail > a_pVal && *a_pVal == '"' && *pTrail == '"') {
1779 ++a_pVal;
1780 *pTrail = 0;
1781 }
1782 }
1783 } else {
1784 // no value to process, just prepare for the next
1785 if (*a_pData) {
1786 SkipNewLine(a_pData);
1787 }
1788 *pTrail = 0;
1789 a_pVal = NULL;
1790 }
1791
1792 // return the standard entry
1793 return true;
1794 }
1795
1796 return false;
1797}
1798
1799template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1801 const SI_CHAR *a_pVal) const {
1802 // check for the "<<<" prefix for a multi-line entry
1803 if (*a_pVal++ != '<')
1804 return false;
1805 if (*a_pVal++ != '<')
1806 return false;
1807 if (*a_pVal++ != '<')
1808 return false;
1809 return true;
1810}
1811
1812template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1814 const SI_CHAR *a_pData) const {
1815 // data is multi-line if it has any of the following features:
1816 // * whitespace prefix
1817 // * embedded newlines
1818 // * whitespace suffix
1819
1820 // empty string
1821 if (!*a_pData) {
1822 return false;
1823 }
1824
1825 // check for prefix
1826 if (IsSpace(*a_pData)) {
1827 return true;
1828 }
1829
1830 // embedded newlines
1831 const SI_CHAR *pStart = a_pData;
1832 while (*a_pData) {
1833 if (IsNewLineChar(*a_pData)) {
1834 return true;
1835 }
1836 ++a_pData;
1837 }
1838
1839 // check for suffix (ensure we don't go before start of string)
1840 if (a_pData > pStart && IsSpace(*(a_pData - 1))) {
1841 return true;
1842 }
1843
1844 return false;
1845}
1846
1847template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1849 const SI_CHAR *a_pText, const SI_CHAR *a_pTag) const {
1850 if (!a_pText || !a_pTag) {
1851 return false;
1852 }
1853 const SI_CHAR *p = a_pText;
1854 while (*p) {
1855 const SI_CHAR *pEndOfLine = p;
1856 while (*pEndOfLine && *pEndOfLine != '\n') {
1857 ++pEndOfLine;
1858 }
1859 const std::basic_string<SI_CHAR> line(p,
1860 static_cast<size_t>(pEndOfLine - p));
1861 if (!IsLess(line.c_str(), a_pTag) && !IsLess(a_pTag, line.c_str())) {
1862 return true;
1863 }
1864 p = pEndOfLine;
1865 if (*p == '\n') {
1866 ++p;
1867 }
1868 }
1869 return false;
1870}
1871
1872template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1874 IsSingleLineQuotedValue(const SI_CHAR *a_pData) const {
1875 // data needs quoting if it starts or ends with whitespace
1876 // and doesn't have embedded newlines
1877
1878 // empty string
1879 if (!*a_pData) {
1880 return false;
1881 }
1882
1883 // embedded newlines
1884 const SI_CHAR *pStart = a_pData;
1885 while (*a_pData) {
1886 if (IsNewLineChar(*a_pData)) {
1887 return false;
1888 }
1889 ++a_pData;
1890 }
1891
1892 // check for prefix only after ruling out embedded newlines
1893 if (IsSpace(*pStart)) {
1894 return true;
1895 }
1896
1897 // check for suffix (ensure we don't go before start of string)
1898 if (a_pData > pStart && IsSpace(*(a_pData - 1))) {
1899 return true;
1900 }
1901
1902 // a value that is itself quoted needs wrapping, or the loader strips its quotes
1903 if (a_pData > pStart + 1 && *pStart == '"' && *(a_pData - 1) == '"') {
1904 return true;
1905 }
1906
1907 return false;
1908}
1909
1910template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1912 SI_CHAR a_c) const {
1913 return (a_c == '\n' || a_c == '\r');
1914}
1915
1916template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1918 SI_CHAR *&a_pData, const SI_CHAR *&a_pVal, const SI_CHAR *a_pTagName,
1919 bool a_bAllowBlankLinesInComment) const {
1920 // we modify this data to strip all newlines down to a single '\n'
1921 // character. This means that on Windows we need to strip out some
1922 // characters which will make the data shorter.
1923 // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
1924 // LINE1-LINE1\nLINE2-LINE2\0
1925 // The pDataLine entry is the pointer to the location in memory that
1926 // the current line needs to start to run following the existing one.
1927 // This may be the same as pCurrLine in which case no move is needed.
1928 SI_CHAR *pDataLine = a_pData;
1929 SI_CHAR *pCurrLine;
1930
1931 // value starts at the current line
1932 a_pVal = a_pData;
1933
1934 // find the end tag. This tag must start in column 1 and be
1935 // followed by a newline. We ignore any whitespace after the end
1936 // tag but not whitespace before it.
1937 SI_CHAR cEndOfLineChar = *a_pData;
1938 for (;;) {
1939 // if we are loading comments then we need a comment character as
1940 // the first character on every line
1941 if (!a_pTagName && !IsComment(*a_pData)) {
1942 // if we aren't allowing blank lines then we're done
1943 if (!a_bAllowBlankLinesInComment) {
1944 break;
1945 }
1946
1947 // if we are allowing blank lines then we only include them
1948 // in this comment if another comment follows, so read ahead
1949 // to find out.
1950 SI_CHAR *pCurr = a_pData;
1951 int nNewLines = 0;
1952 while (IsSpace(*pCurr)) {
1953 if (IsNewLineChar(*pCurr)) {
1954 ++nNewLines;
1955 SkipNewLine(pCurr);
1956 } else {
1957 ++pCurr;
1958 }
1959 }
1960
1961 // we have a comment, add the blank lines to the output
1962 // and continue processing from here
1963 if (IsComment(*pCurr)) {
1964 for (; nNewLines > 0; --nNewLines)
1965 *pDataLine++ = '\n';
1966 a_pData = pCurr;
1967 continue;
1968 }
1969
1970 // the comment ends here
1971 break;
1972 }
1973
1974 // find the end of this line
1975 pCurrLine = a_pData;
1976 while (*a_pData && !IsNewLineChar(*a_pData))
1977 ++a_pData;
1978
1979 // move this line down to the location that it should be if necessary
1980 if (pDataLine < pCurrLine) {
1981 size_t nLen = (size_t)(a_pData - pCurrLine);
1982 memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
1983 pDataLine[nLen] = '\0';
1984 }
1985
1986 // end the line with a NULL
1987 cEndOfLineChar = *a_pData;
1988 *a_pData = 0;
1989
1990 // if are looking for a tag then do the check now. This is done before
1991 // checking for end of the data, so that if we have the tag at the end
1992 // of the data then the tag is removed correctly.
1993 if (a_pTagName) {
1994 // strip whitespace from the end of this tag
1995 SI_CHAR *pc = a_pData - 1;
1996 while (pc > pDataLine && IsSpace(*pc))
1997 --pc;
1998 SI_CHAR ch = *++pc;
1999 *pc = 0;
2000
2001 if (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)) {
2002 break;
2003 }
2004
2005 *pc = ch;
2006 }
2007
2008 // if we are at the end of the data then we just automatically end
2009 // this entry and return the current data.
2010 if (!cEndOfLineChar) {
2011 return true;
2012 }
2013
2014 // otherwise we need to process this newline to ensure that it consists
2015 // of just a single \n character.
2016 pDataLine += (a_pData - pCurrLine);
2017 *a_pData = cEndOfLineChar;
2018 SkipNewLine(a_pData);
2019 *pDataLine++ = '\n';
2020 }
2021
2022 // if we didn't find a comment at all then return false
2023 if (a_pVal == a_pData) {
2024 a_pVal = NULL;
2025 return false;
2026 }
2027
2028 // the data (which ends at the end of the last line) needs to be
2029 // null-terminated BEFORE before the newline character(s). If the
2030 // user wants a new line in the multi-line data then they need to
2031 // add an empty line before the tag.
2032 *--pDataLine = '\0';
2033
2034 // if looking for a tag and if we aren't at the end of the data,
2035 // then move a_pData to the start of the next line.
2036 if (a_pTagName && cEndOfLineChar) {
2037 SI_ASSERT(IsNewLineChar(cEndOfLineChar));
2038 *a_pData = cEndOfLineChar;
2039 SkipNewLine(a_pData);
2040 }
2041
2042 return true;
2043}
2044
2045template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2047 const SI_CHAR *&a_pString) {
2048 size_t uLen = 0;
2049 if (sizeof(SI_CHAR) == sizeof(char)) {
2050 uLen = strlen((const char *)a_pString);
2051 } else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
2052 uLen = wcslen((const wchar_t *)a_pString);
2053 } else {
2054 for (; a_pString[uLen]; ++uLen) /*loop*/
2055 ;
2056 }
2057 if (uLen >= (SI_MAX_FILE_SIZE / sizeof(SI_CHAR))) {
2058 return SI_NOMEM;
2059 }
2060 ++uLen; // NULL character
2061 SI_CHAR *pCopy = new (std::nothrow) SI_CHAR[uLen];
2062 if (!pCopy) {
2063 return SI_NOMEM;
2064 }
2065 memcpy(pCopy, a_pString, sizeof(SI_CHAR) * uLen);
2066 m_strings.push_back(pCopy);
2067 a_pString = pCopy;
2068 return SI_OK;
2069}
2070
2071template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2073 UndoIncrementalLoadData(const TNamesDepend &a_oAddedSections,
2074 const TNamesDepend &a_oAddedKeys) {
2075 typename TNamesDepend::const_iterator iAddedKey = a_oAddedKeys.begin();
2076 for (; iAddedKey != a_oAddedKeys.end(); ++iAddedKey) {
2077 Delete(iAddedKey->pComment, iAddedKey->pItem, false);
2078 }
2079
2080 typename TNamesDepend::const_iterator iAddedSection =
2081 a_oAddedSections.begin();
2082 for (; iAddedSection != a_oAddedSections.end(); ++iAddedSection) {
2083 Delete(iAddedSection->pItem, NULL, false);
2084 }
2085}
2086
2087template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2089 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pValue,
2090 const SI_CHAR *a_pComment, bool a_bForceReplace, bool a_bCopyStrings) {
2091 SI_Error rc;
2092 bool bInserted = false;
2093 bool bInsertedSection = false;
2094 bool bCopiedComment = false;
2095 bool bCopiedSection = false;
2096 bool bCopiedKey = false;
2097
2098 SI_ASSERT(!a_pComment || IsComment(*a_pComment));
2099
2100 // if we are copying strings then make a copy of the comment now
2101 // because we will need it when we add the entry.
2102 if (a_bCopyStrings && a_pComment) {
2103 rc = CopyString(a_pComment);
2104 if (rc < 0)
2105 return rc;
2106 bCopiedComment = true;
2107 }
2108
2109 // create the section entry if necessary
2110 typename TSection::iterator iSection = m_data.find(a_pSection);
2111 if (iSection == m_data.end()) {
2112 // if the section doesn't exist then we need a copy as the
2113 // string needs to last beyond the end of this function
2114 if (a_bCopyStrings) {
2115 rc = CopyString(a_pSection);
2116 if (rc < 0) {
2117 if (bCopiedComment) {
2118 DeleteString(a_pComment);
2119 }
2120 return rc;
2121 }
2122 bCopiedSection = true;
2123 }
2124
2125 // only set the comment if this is a section only entry
2126 Entry oSection(a_pSection, ++m_nOrder);
2127 if (a_pComment && !a_pKey) {
2128 oSection.pComment = a_pComment;
2129 }
2130
2131 typename TSection::value_type oEntry(oSection, TKeyVal());
2132 typedef typename TSection::iterator SectionIterator;
2133 std::pair<SectionIterator, bool> i = m_data.insert(oEntry);
2134 iSection = i.first;
2135 bInserted = true;
2136 bInsertedSection = true;
2137 }
2138 if (!a_pKey) {
2139 // section only entries are specified with pItem as NULL
2140 return bInserted ? SI_INSERTED : SI_UPDATED;
2141 }
2142
2143 // check for existence of the key
2144 TKeyVal &keyval = iSection->second;
2145 typename TKeyVal::iterator iKey = keyval.find(a_pKey);
2146 bInserted = iKey == keyval.end();
2147
2148 // remove all existing entries but save the load order and
2149 // comment of the first entry
2150 int nLoadOrder = ++m_nOrder;
2151 if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) {
2152 const SI_CHAR *pComment = NULL;
2153 while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) {
2154 if (iKey->first.nOrder < nLoadOrder) {
2155 nLoadOrder = iKey->first.nOrder;
2156 pComment = iKey->first.pComment;
2157 }
2158 ++iKey;
2159 }
2160 if (pComment) {
2161 DeleteString(a_pComment);
2162 a_pComment = pComment;
2163 rc = CopyString(a_pComment);
2164 if (rc < 0)
2165 return rc;
2166 }
2167 Delete(a_pSection, a_pKey);
2168 iKey = keyval.end();
2169 }
2170
2171 // values need to be a valid string, even if they are an empty string
2172 if (!a_pValue) {
2173 a_pValue = &m_cEmptyString;
2174 }
2175
2176 // make string copies if necessary
2177 bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
2178 if (a_bCopyStrings) {
2179 if (bForceCreateNewKey || iKey == keyval.end()) {
2180 // if the key doesn't exist then we need a copy as the
2181 // string needs to last beyond the end of this function
2182 // because we will be inserting the key next
2183 rc = CopyString(a_pKey);
2184 if (rc < 0) {
2185 if (bInsertedSection) {
2186 m_data.erase(iSection);
2187 }
2188 if (bCopiedSection) {
2189 DeleteString(a_pSection);
2190 }
2191 if (bCopiedComment) {
2192 DeleteString(a_pComment);
2193 }
2194 return rc;
2195 }
2196 bCopiedKey = true;
2197 }
2198
2199 // we always need a copy of the value
2200 rc = CopyString(a_pValue);
2201 if (rc < 0) {
2202 if (bCopiedKey) {
2203 DeleteString(a_pKey);
2204 }
2205 if (bInsertedSection) {
2206 m_data.erase(iSection);
2207 }
2208 if (bCopiedSection) {
2209 DeleteString(a_pSection);
2210 }
2211 if (bCopiedComment) {
2212 DeleteString(a_pComment);
2213 }
2214 return rc;
2215 }
2216 }
2217
2218 // create the key entry
2219 if (iKey == keyval.end() || bForceCreateNewKey) {
2220 Entry oKey(a_pKey, nLoadOrder);
2221 if (a_pComment) {
2222 oKey.pComment = a_pComment;
2223 }
2224 typename TKeyVal::value_type oEntry(oKey,
2225 static_cast<const SI_CHAR *>(NULL));
2226 iKey = keyval.insert(oEntry);
2227 } else {
2228 DeleteString(iKey->second);
2229 }
2230
2231 iKey->second = a_pValue;
2232 return bInserted ? SI_INSERTED : SI_UPDATED;
2233}
2234
2235template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2237 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pDefault,
2238 bool *a_pHasMultiple) const {
2239 if (a_pHasMultiple) {
2240 *a_pHasMultiple = false;
2241 }
2242 if (!a_pSection || !a_pKey) {
2243 return a_pDefault;
2244 }
2245 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2246 if (iSection == m_data.end()) {
2247 return a_pDefault;
2248 }
2249 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2250 if (iKeyVal == iSection->second.end()) {
2251 return a_pDefault;
2252 }
2253
2254 // check for multiple entries with the same key
2255 if (m_bAllowMultiKey && a_pHasMultiple) {
2256 typename TKeyVal::const_iterator iTemp = iKeyVal;
2257 if (++iTemp != iSection->second.end()) {
2258 if (!IsLess(a_pKey, iTemp->first.pItem)) {
2259 *a_pHasMultiple = true;
2260 }
2261 }
2262 }
2263
2264 return iKeyVal->second;
2265}
2266
2267template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2269 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, long a_nDefault,
2270 bool *a_pHasMultiple) const {
2271 // return the default if we don't have a value
2272 const SI_CHAR *pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2273 if (!pszValue || !*pszValue)
2274 return a_nDefault;
2275
2276 // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2277 char szValue[64] = {0};
2278 SI_CONVERTER c(m_bStoreIsUtf8);
2279 if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2280 return a_nDefault;
2281 }
2282
2283 // handle the value as hex if prefaced with "0x"
2284 long nValue = a_nDefault;
2285 char *pszSuffix = szValue;
2286 errno = 0;
2287 if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
2288 if (!szValue[2])
2289 return a_nDefault;
2290 nValue = strtol(&szValue[2], &pszSuffix, 16);
2291 } else {
2292 nValue = strtol(szValue, &pszSuffix, 10);
2293 }
2294
2295 // any invalid strings will return the default value
2296 if (*pszSuffix || errno == ERANGE) {
2297 return a_nDefault;
2298 }
2299
2300 return nValue;
2301}
2302
2303template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2305 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, long a_nValue,
2306 const SI_CHAR *a_pComment, bool a_bUseHex, bool a_bForceReplace) {
2307 // use SetValue to create sections
2308 if (!a_pSection || !a_pKey)
2309 return SI_FAIL;
2310
2311 // convert to an ASCII string
2312 char szInput[64];
2313#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2314 sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2315#else // !__STDC_WANT_SECURE_LIB__
2316 snprintf(szInput, sizeof(szInput), a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2317#endif // __STDC_WANT_SECURE_LIB__
2318
2319 // convert to output text
2320 SI_CHAR szOutput[64];
2321 SI_CONVERTER c(m_bStoreIsUtf8);
2322 if (!c.ConvertFromStore(szInput, strlen(szInput) + 1, szOutput,
2323 sizeof(szOutput) / sizeof(SI_CHAR))) {
2324 return SI_FAIL;
2325 }
2326
2327 // actually add it
2328 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace,
2329 true);
2330}
2331
2332template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2334 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, double a_nDefault,
2335 bool *a_pHasMultiple) const {
2336 // return the default if we don't have a value
2337 const SI_CHAR *pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2338 if (!pszValue || !*pszValue)
2339 return a_nDefault;
2340
2341 // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2342 char szValue[64] = {0};
2343 SI_CONVERTER c(m_bStoreIsUtf8);
2344 if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2345 return a_nDefault;
2346 }
2347
2348 char *pszSuffix = szValue;
2349 double nValue = strtod(szValue, &pszSuffix);
2350
2351 // any invalid strings will return the default value
2352 // check if no conversion was performed or if there are trailing characters
2353 if (pszSuffix == szValue || *pszSuffix) {
2354 return a_nDefault;
2355 }
2356
2357 return nValue;
2358}
2359
2360template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2362 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, double a_nValue,
2363 const SI_CHAR *a_pComment, bool a_bForceReplace) {
2364 // use SetValue to create sections
2365 if (!a_pSection || !a_pKey)
2366 return SI_FAIL;
2367
2368 // convert to an ASCII string
2369 char szInput[64];
2370#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2371 sprintf_s(szInput, "%.17g", a_nValue);
2372#else // !__STDC_WANT_SECURE_LIB__
2373 snprintf(szInput, sizeof(szInput), "%.17g", a_nValue);
2374#endif // __STDC_WANT_SECURE_LIB__
2375
2376 // convert to output text
2377 SI_CHAR szOutput[64];
2378 SI_CONVERTER c(m_bStoreIsUtf8);
2379 if (!c.ConvertFromStore(szInput, strlen(szInput) + 1, szOutput,
2380 sizeof(szOutput) / sizeof(SI_CHAR))) {
2381 return SI_FAIL;
2382 }
2383
2384 // actually add it
2385 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace,
2386 true);
2387}
2388
2389template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2391 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bDefault,
2392 bool *a_pHasMultiple) const {
2393 // return the default if we don't have a value
2394 const SI_CHAR *pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2395 if (!pszValue || !*pszValue)
2396 return a_bDefault;
2397
2398 // we only look at the minimum number of characters
2399 switch (pszValue[0]) {
2400 case 't':
2401 case 'T': // true
2402 case 'y':
2403 case 'Y': // yes
2404 case '1': // 1 (one)
2405 return true;
2406
2407 case 'f':
2408 case 'F': // false
2409 case 'n':
2410 case 'N': // no
2411 case '0': // 0 (zero)
2412 return false;
2413
2414 case 'o':
2415 case 'O':
2416 if (pszValue[1] == 'n' || pszValue[1] == 'N')
2417 return true; // on
2418 if (pszValue[1] == 'f' || pszValue[1] == 'F')
2419 return false; // off
2420 break;
2421 }
2422
2423 // no recognized value, return the default
2424 return a_bDefault;
2425}
2426
2427template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2429 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bValue,
2430 const SI_CHAR *a_pComment, bool a_bForceReplace) {
2431 // use SetValue to create sections
2432 if (!a_pSection || !a_pKey)
2433 return SI_FAIL;
2434
2435 // convert to an ASCII string
2436 const char *pszInput = a_bValue ? "true" : "false";
2437
2438 // convert to output text
2439 SI_CHAR szOutput[64];
2440 SI_CONVERTER c(m_bStoreIsUtf8);
2441 if (!c.ConvertFromStore(pszInput, strlen(pszInput) + 1, szOutput,
2442 sizeof(szOutput) / sizeof(SI_CHAR))) {
2443 return SI_FAIL;
2444 }
2445
2446 // actually add it
2447 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace,
2448 true);
2449}
2450
2451template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2453 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey,
2454 TNamesDepend &a_values) const {
2455 a_values.clear();
2456
2457 if (!a_pSection || !a_pKey) {
2458 return false;
2459 }
2460 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2461 if (iSection == m_data.end()) {
2462 return false;
2463 }
2464 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2465 if (iKeyVal == iSection->second.end()) {
2466 return false;
2467 }
2468
2469 // insert all values for this key
2470 a_values.push_back(
2471 Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2472 if (m_bAllowMultiKey) {
2473 ++iKeyVal;
2474 while (iKeyVal != iSection->second.end() &&
2475 !IsLess(a_pKey, iKeyVal->first.pItem)) {
2476 a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment,
2477 iKeyVal->first.nOrder));
2478 ++iKeyVal;
2479 }
2480 }
2481
2482 return true;
2483}
2484
2485template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2487 const SI_CHAR *a_pSection) const {
2488 if (!a_pSection) {
2489 return -1;
2490 }
2491
2492 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2493 if (iSection == m_data.end()) {
2494 return -1;
2495 }
2496 const TKeyVal &section = iSection->second;
2497
2498 // if multi-key isn't permitted then the section size is
2499 // the number of keys that we have.
2500 if (!m_bAllowMultiKey || section.empty()) {
2501 return (int)section.size();
2502 }
2503
2504 // otherwise we need to count them
2505 int nCount = 0;
2506 const SI_CHAR *pLastKey = NULL;
2507 typename TKeyVal::const_iterator iKeyVal = section.begin();
2508 for (; iKeyVal != section.end(); ++iKeyVal) {
2509 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2510 ++nCount;
2511 pLastKey = iKeyVal->first.pItem;
2512 }
2513 }
2514 return nCount;
2515}
2516
2517template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2520 const SI_CHAR *a_pSection) const {
2521 if (a_pSection) {
2522 typename TSection::const_iterator i = m_data.find(a_pSection);
2523 if (i != m_data.end()) {
2524 return &(i->second);
2525 }
2526 }
2527 return 0;
2528}
2529
2530template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2532 TNamesDepend &a_names) const {
2533 a_names.clear();
2534 typename TSection::const_iterator i = m_data.begin();
2535 for (; i != m_data.end(); ++i) {
2536 a_names.push_back(i->first);
2537 }
2538}
2539
2540template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2542 const SI_CHAR *a_pSection, TNamesDepend &a_names) const {
2543 a_names.clear();
2544
2545 if (!a_pSection) {
2546 return false;
2547 }
2548
2549 typename TSection::const_iterator iSection = m_data.find(a_pSection);
2550 if (iSection == m_data.end()) {
2551 return false;
2552 }
2553
2554 const TKeyVal &section = iSection->second;
2555 const SI_CHAR *pLastKey = NULL;
2556 typename TKeyVal::const_iterator iKeyVal = section.begin();
2557 for (; iKeyVal != section.end(); ++iKeyVal) {
2558 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2559 a_names.push_back(iKeyVal->first);
2560 pLastKey = iKeyVal->first.pItem;
2561 }
2562 }
2563
2564 return true;
2565}
2566
2567template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2569 const char *a_pszFile, bool a_bAddSignature) const {
2570 FILE *fp = NULL;
2571#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2572 fopen_s(&fp, a_pszFile, "wb");
2573#else // !__STDC_WANT_SECURE_LIB__
2574 fp = fopen(a_pszFile, "wb");
2575#endif // __STDC_WANT_SECURE_LIB__
2576 if (!fp)
2577 return SI_FILE;
2578 SI_Error rc = SaveFile(fp, a_bAddSignature);
2579 fclose(fp);
2580 return rc;
2581}
2582
2583#ifdef SI_HAS_WIDE_FILE
2584template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2586 const SI_WCHAR_T *a_pwszFile, bool a_bAddSignature) const {
2587#ifdef _WIN32
2588 FILE *fp = NULL;
2589#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2590 _wfopen_s(&fp, a_pwszFile, L"wb");
2591#else // !__STDC_WANT_SECURE_LIB__
2592 fp = _wfopen(a_pwszFile, L"wb");
2593#endif // __STDC_WANT_SECURE_LIB__
2594 if (!fp)
2595 return SI_FILE;
2596 SI_Error rc = SaveFile(fp, a_bAddSignature);
2597 fclose(fp);
2598 return rc;
2599#else // !_WIN32 (therefore SI_CONVERT_ICU)
2600 char *szFile = NULL;
2601 SI_Error rcPath = SI_Internal::UCharPathToUtf8(a_pwszFile, szFile);
2602 if (rcPath < 0) {
2603 return rcPath;
2604 }
2605 SI_Error rc = SaveFile(szFile, a_bAddSignature);
2606 delete[] szFile;
2607 return rc;
2608#endif // _WIN32
2609}
2610#endif // SI_HAS_WIDE_FILE
2611
2612template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2614 FILE *a_pFile, bool a_bAddSignature) const {
2615 FileWriter writer(a_pFile);
2616 return Save(writer, a_bAddSignature);
2617}
2618
2619template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2621 OutputWriter &a_oOutput, bool a_bAddSignature) const {
2622 Converter convert(m_bStoreIsUtf8);
2623
2624 // add the UTF-8 signature if it is desired
2625 if (m_bStoreIsUtf8 && a_bAddSignature) {
2626 a_oOutput.Write(SI_UTF8_SIGNATURE);
2627 }
2628
2629 // get all of the sections sorted in load order
2630 TNamesDepend oSections;
2631 GetAllSections(oSections);
2632#if defined(_MSC_VER) && _MSC_VER <= 1200
2633 oSections.sort();
2634#elif defined(__BORLANDC__)
2635 oSections.sort(Entry::LoadOrder());
2636#else
2637 oSections.sort(typename Entry::LoadOrder());
2638#endif
2639
2640 // if there is an empty section name, then it must be written out first
2641 // regardless of the load order
2642 typename TNamesDepend::iterator is = oSections.begin();
2643 for (; is != oSections.end(); ++is) {
2644 if (!*is->pItem) {
2645 // move the empty section name to the front of the section list
2646 if (is != oSections.begin()) {
2647 oSections.splice(oSections.begin(), oSections, is, std::next(is));
2648 }
2649 break;
2650 }
2651 }
2652
2653 // write the file comment if we have one
2654 bool bNeedNewLine = false;
2655 if (m_pFileComment) {
2656 if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
2657 return SI_FAIL;
2658 }
2659 bNeedNewLine = true;
2660 }
2661
2662 // iterate through our sections and output the data
2663 typename TNamesDepend::const_iterator iSection = oSections.begin();
2664 for (; iSection != oSections.end(); ++iSection) {
2665 // write out the comment if there is one
2666 if (iSection->pComment) {
2667 if (bNeedNewLine) {
2668 a_oOutput.Write(SI_NEWLINE_A);
2669 a_oOutput.Write(SI_NEWLINE_A);
2670 }
2671 if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) {
2672 return SI_FAIL;
2673 }
2674 bNeedNewLine = false;
2675 }
2676
2677 if (bNeedNewLine) {
2678 a_oOutput.Write(SI_NEWLINE_A);
2679 a_oOutput.Write(SI_NEWLINE_A);
2680 bNeedNewLine = false;
2681 }
2682
2683 // write the section (unless there is no section name)
2684 if (*iSection->pItem) {
2685 if (!convert.ConvertToStore(iSection->pItem)) {
2686 return SI_FAIL;
2687 }
2688 a_oOutput.Write("[");
2689 a_oOutput.Write(convert.Data());
2690 a_oOutput.Write("]");
2691 a_oOutput.Write(SI_NEWLINE_A);
2692 }
2693
2694 // get all of the keys sorted in load order
2695 TNamesDepend oKeys;
2696 GetAllKeys(iSection->pItem, oKeys);
2697#if defined(_MSC_VER) && _MSC_VER <= 1200
2698 oKeys.sort();
2699#elif defined(__BORLANDC__)
2700 oKeys.sort(Entry::LoadOrder());
2701#else
2702 oKeys.sort(typename Entry::LoadOrder());
2703#endif
2704
2705 // write all keys and values
2706 typename TNamesDepend::const_iterator iKey = oKeys.begin();
2707 for (; iKey != oKeys.end(); ++iKey) {
2708 // get all values for this key
2709 TNamesDepend oValues;
2710 GetAllValues(iSection->pItem, iKey->pItem, oValues);
2711
2712 typename TNamesDepend::const_iterator iValue = oValues.begin();
2713 for (; iValue != oValues.end(); ++iValue) {
2714 // write out the comment if there is one
2715 if (iValue->pComment) {
2716 a_oOutput.Write(SI_NEWLINE_A);
2717 if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) {
2718 return SI_FAIL;
2719 }
2720 }
2721
2722 // write the key
2723 if (!convert.ConvertToStore(iKey->pItem)) {
2724 return SI_FAIL;
2725 }
2726 a_oOutput.Write(convert.Data());
2727
2728 // write the value as long
2729 if (*iValue->pItem || !m_bAllowKeyOnly) {
2730 if (!convert.ConvertToStore(iValue->pItem)) {
2731 return SI_FAIL;
2732 }
2733 a_oOutput.Write(m_bSpaces ? " = " : "=");
2734 if (m_bParseQuotes && IsSingleLineQuotedValue(iValue->pItem)) {
2735 // the only way to preserve external whitespace on a value (i.e. before or after)
2736 // is to quote it. This is simple quoting, we don't escape quotes within the data.
2737 a_oOutput.Write("\"");
2738 a_oOutput.Write(convert.Data());
2739 a_oOutput.Write("\"");
2740 } else if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
2741 // multi-line data needs to be processed specially to ensure
2742 // that we use the correct newline format for the current system
2743 char szTag[64];
2744 SI_CHAR szTagSi[64];
2745 int nSuffix = 0;
2746 for (;;) {
2747 if (nSuffix == 0) {
2748#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2749 sprintf_s(szTag, "END_OF_TEXT");
2750#else
2751 snprintf(szTag, sizeof(szTag), "END_OF_TEXT");
2752#endif
2753 } else {
2754#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2755 sprintf_s(szTag, "END_OF_TEXT_%d", nSuffix);
2756#else
2757 snprintf(szTag, sizeof(szTag), "END_OF_TEXT_%d", nSuffix);
2758#endif
2759 }
2760 size_t u = 0;
2761 for (; szTag[u] && u + 1 < sizeof(szTagSi) / sizeof(szTagSi[0]);
2762 ++u) {
2763 szTagSi[u] = static_cast<SI_CHAR>(szTag[u]);
2764 }
2765 szTagSi[u] = 0;
2766 if (!MultiLineTextHasTag(iValue->pItem, szTagSi)) {
2767 break;
2768 }
2769 ++nSuffix;
2770 }
2771 a_oOutput.Write("<<<");
2772 a_oOutput.Write(szTag);
2773 a_oOutput.Write(SI_NEWLINE_A);
2774 if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
2775 return SI_FAIL;
2776 }
2777 a_oOutput.Write(szTag);
2778 } else {
2779 a_oOutput.Write(convert.Data());
2780 }
2781 }
2782 a_oOutput.Write(SI_NEWLINE_A);
2783 }
2784 }
2785
2786 bNeedNewLine = true;
2787 }
2788
2789 return SI_OK;
2790}
2791
2792template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2794 OutputWriter &a_oOutput, Converter &a_oConverter,
2795 const SI_CHAR *a_pText) const {
2796 const SI_CHAR *pEndOfLine;
2797 SI_CHAR cEndOfLineChar = *a_pText;
2798 while (cEndOfLineChar) {
2799 // find the end of this line
2800 pEndOfLine = a_pText;
2801 for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/
2802 ;
2803 cEndOfLineChar = *pEndOfLine;
2804
2805 const std::basic_string<SI_CHAR> line(
2806 a_pText, static_cast<size_t>(pEndOfLine - a_pText));
2807 if (!a_oConverter.ConvertToStore(line.c_str())) {
2808 return false;
2809 }
2810 a_pText += (pEndOfLine - a_pText) + 1;
2811 a_oOutput.Write(a_oConverter.Data());
2812 a_oOutput.Write(SI_NEWLINE_A);
2813 }
2814 return true;
2815}
2816
2817template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2819 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bRemoveEmpty) {
2820 return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty);
2821}
2822
2823template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2825 const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pValue,
2826 bool a_bRemoveEmpty) {
2827 if (!a_pSection) {
2828 return false;
2829 }
2830
2831 typename TSection::iterator iSection = m_data.find(a_pSection);
2832 if (iSection == m_data.end()) {
2833 return false;
2834 }
2835
2836 // remove a single key if we have a keyname
2837 if (a_pKey) {
2838 typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
2839 if (iKeyVal == iSection->second.end()) {
2840 return false;
2841 }
2842
2843 const static SI_STRLESS isLess = SI_STRLESS();
2844
2845 // remove any copied strings and then the key
2846 typename TKeyVal::iterator iDelete;
2847 bool bDeleted = false;
2848 do {
2849 iDelete = iKeyVal++;
2850
2851 if (a_pValue == NULL || (isLess(a_pValue, iDelete->second) == false &&
2852 isLess(iDelete->second, a_pValue) == false)) {
2853 DeleteString(iDelete->first.pItem);
2854 DeleteString(iDelete->first.pComment);
2855 DeleteString(iDelete->second);
2856 iSection->second.erase(iDelete);
2857 bDeleted = true;
2858 }
2859 } while (iKeyVal != iSection->second.end() &&
2860 !IsLess(a_pKey, iKeyVal->first.pItem));
2861
2862 if (!bDeleted) {
2863 return false;
2864 }
2865
2866 // done now if the section is not empty or we are not pruning away
2867 // the empty sections. Otherwise let it fall through into the section
2868 // deletion code
2869 if (!a_bRemoveEmpty || !iSection->second.empty()) {
2870 return true;
2871 }
2872 } else {
2873 // delete all copied strings from this section. The actual
2874 // entries will be removed when the section is removed.
2875 typename TKeyVal::iterator iKeyVal = iSection->second.begin();
2876 for (; iKeyVal != iSection->second.end(); ++iKeyVal) {
2877 DeleteString(iKeyVal->first.pItem);
2878 DeleteString(iKeyVal->first.pComment);
2879 DeleteString(iKeyVal->second);
2880 }
2881 }
2882
2883 // delete the section itself
2884 DeleteString(iSection->first.pItem);
2885 DeleteString(iSection->first.pComment);
2886 m_data.erase(iSection);
2887
2888 return true;
2889}
2890
2891template <class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2893 const SI_CHAR *a_pString) {
2894 if (!a_pString) {
2895 return;
2896 }
2897 // strings may exist either inside the data block, or they will be
2898 // individually allocated and stored in m_strings. We only physically
2899 // delete those stored in m_strings.
2900 if (!m_pData || a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
2901 typename TNamesDepend::iterator i = m_strings.begin();
2902 for (; i != m_strings.end(); ++i) {
2903 if (a_pString == i->pItem) {
2904 delete[] const_cast<SI_CHAR *>(i->pItem);
2905 m_strings.erase(i);
2906 break;
2907 }
2908 }
2909 }
2910}
2911
2912// ---------------------------------------------------------------------------
2913// CONVERSION FUNCTIONS
2914// ---------------------------------------------------------------------------
2915
2916// Defines the conversion classes for different libraries. Before including
2917// SimpleIni.h, set the converter that you wish you use by defining one of the
2918// following symbols.
2919//
2920// SI_NO_CONVERSION Do not make the "W" wide character version of the
2921// library available. Only CSimpleIniA etc is defined.
2922// Default on Linux/MacOS/etc.
2923// SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
2924// Default on Windows.
2925// SI_CONVERT_GENERIC Use built-in locale-independent UTF-8 conversion
2926// (no additional source files required)
2927// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
2928// ICU headers on include path and icuuc.lib
2929
2930#if !defined(SI_NO_CONVERSION) && !defined(SI_CONVERT_GENERIC) && \
2931 !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
2932#ifdef _WIN32
2933#define SI_CONVERT_WIN32
2934#else
2935#define SI_NO_CONVERSION
2936#endif
2937#endif
2938
2944template <class SI_CHAR> struct SI_GenericCase {
2945 bool operator()(const SI_CHAR *pLeft, const SI_CHAR *pRight) const {
2946 long cmp;
2947 for (; *pLeft && *pRight; ++pLeft, ++pRight) {
2948 cmp = (long)*pLeft - (long)*pRight;
2949 if (cmp != 0) {
2950 return cmp < 0;
2951 }
2952 }
2953 return *pRight != 0;
2954 }
2955};
2956
2963template <class SI_CHAR> struct SI_GenericNoCase {
2964 inline SI_CHAR locase(SI_CHAR ch) const {
2965 return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
2966 }
2967 bool operator()(const SI_CHAR *pLeft, const SI_CHAR *pRight) const {
2968 long cmp;
2969 for (; *pLeft && *pRight; ++pLeft, ++pRight) {
2970 cmp = (long)locase(*pLeft) - (long)locase(*pRight);
2971 if (cmp != 0) {
2972 return cmp < 0;
2973 }
2974 }
2975 return *pRight != 0;
2976 }
2977};
2978
2982template <class SI_CHAR> class SI_ConvertA {
2983 bool m_bStoreIsUtf8;
2984
2985protected:
2986 SI_ConvertA() {}
2987
2988public:
2989 SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) {}
2990
2991 /* copy and assignment */
2992 SI_ConvertA(const SI_ConvertA &rhs) { operator=(rhs); }
2993 SI_ConvertA &operator=(const SI_ConvertA &rhs) {
2994 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2995 return *this;
2996 }
2997
3011 size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen) {
3012 (void)a_pInputData;
3013 SI_ASSERT(a_uInputDataLen != (size_t)-1);
3014
3015 // ASCII/MBCS/UTF-8 needs no conversion
3016 return a_uInputDataLen;
3017 }
3018
3032 bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen,
3033 SI_CHAR *a_pOutputData, size_t a_uOutputDataSize) {
3034 // ASCII/MBCS/UTF-8 needs no conversion
3035 if (a_uInputDataLen > a_uOutputDataSize) {
3036 return false;
3037 }
3038 memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
3039 return true;
3040 }
3041
3052 size_t SizeToStore(const SI_CHAR *a_pInputData) {
3053 // ASCII/MBCS/UTF-8 needs no conversion
3054 return strlen((const char *)a_pInputData) + 1;
3055 }
3056
3070 bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData,
3071 size_t a_uOutputDataSize) {
3072 // calc input string length (SI_CHAR type and size independent)
3073 size_t uInputLen = strlen((const char *)a_pInputData) + 1;
3074 if (uInputLen > a_uOutputDataSize) {
3075 return false;
3076 }
3077
3078 // ascii/UTF-8 needs no conversion
3079 memcpy(a_pOutputData, a_pInputData, uInputLen);
3080 return true;
3081 }
3082};
3083
3084// ---------------------------------------------------------------------------
3085// SI_CONVERT_GENERIC
3086// ---------------------------------------------------------------------------
3087#ifdef SI_CONVERT_GENERIC
3088
3089#define SI_Case SI_GenericCase
3090#define SI_NoCase SI_GenericNoCase
3091
3092#include <wchar.h>
3093
3094namespace SI_UTF8 {
3095
3096constexpr char32_t REPLACEMENT = 0xFFFD;
3097
3099inline bool Decode(const char *&a_src, const char *a_end, char32_t &a_cp) {
3100 if (a_src >= a_end) {
3101 return false;
3102 }
3103 const unsigned char c0 = (unsigned char)*a_src++;
3104 if (c0 < 0x80) {
3105 a_cp = c0;
3106 return true;
3107 }
3108 if (c0 < 0xC2) {
3109 return false;
3110 }
3111
3112 int extra = 0;
3113 char32_t ch = 0;
3114 if (c0 < 0xE0) {
3115 extra = 1;
3116 ch = c0 & 0x1F;
3117 } else if (c0 < 0xF0) {
3118 extra = 2;
3119 ch = c0 & 0x0F;
3120 } else if (c0 < 0xF5) {
3121 extra = 3;
3122 ch = c0 & 0x07;
3123 } else {
3124 return false;
3125 }
3126 if (a_src + extra > a_end) {
3127 a_src -= 1;
3128 return false;
3129 }
3130
3131 for (int i = 0; i < extra; ++i) {
3132 const unsigned char cx = (unsigned char)*a_src++;
3133 if ((cx & 0xC0) != 0x80) {
3134 a_src -= (i + 2);
3135 return false;
3136 }
3137 ch = (ch << 6) | (cx & 0x3F);
3138 }
3139
3140 if ((extra == 1 && ch < 0x80) || (extra == 2 && ch < 0x800) ||
3141 (extra == 3 && ch < 0x10000) || ch > 0x10FFFF ||
3142 (ch >= 0xD800 && ch <= 0xDFFF)) {
3143 a_src -= (extra + 1);
3144 return false;
3145 }
3146
3147 a_cp = ch;
3148 return true;
3149}
3150
3154inline int Encode(char32_t a_cp, char *a_dst, size_t a_cap) {
3155 if (a_cp >= 0x110000 || (a_cp >= 0xD800 && a_cp <= 0xDFFF)) {
3156 a_cp = REPLACEMENT;
3157 }
3158
3159 if (a_cp < 0x80) {
3160 if (a_cap < 1)
3161 return -1;
3162 a_dst[0] = (char)a_cp;
3163 return 1;
3164 }
3165 if (a_cp < 0x800) {
3166 if (a_cap < 2)
3167 return -1;
3168 a_dst[0] = (char)(0xC0 | (a_cp >> 6));
3169 a_dst[1] = (char)(0x80 | (a_cp & 0x3F));
3170 return 2;
3171 }
3172 if (a_cp < 0x10000) {
3173 if (a_cap < 3)
3174 return -1;
3175 a_dst[0] = (char)(0xE0 | (a_cp >> 12));
3176 a_dst[1] = (char)(0x80 | ((a_cp >> 6) & 0x3F));
3177 a_dst[2] = (char)(0x80 | (a_cp & 0x3F));
3178 return 3;
3179 }
3180 if (a_cap < 4)
3181 return -1;
3182 a_dst[0] = (char)(0xF0 | (a_cp >> 18));
3183 a_dst[1] = (char)(0x80 | ((a_cp >> 12) & 0x3F));
3184 a_dst[2] = (char)(0x80 | ((a_cp >> 6) & 0x3F));
3185 a_dst[3] = (char)(0x80 | (a_cp & 0x3F));
3186 return 4;
3187}
3188
3189template <class SI_CHAR>
3190inline bool AppendCodePoint(char32_t a_cp, SI_CHAR *&a_dst, SI_CHAR *a_dstEnd) {
3191 if (sizeof(SI_CHAR) >= sizeof(char32_t)) {
3192 if (a_dst >= a_dstEnd)
3193 return false;
3194 *a_dst++ = (SI_CHAR)a_cp;
3195 return true;
3196 }
3197 if (sizeof(SI_CHAR) == 2) {
3198 if (a_cp < 0x10000) {
3199 if (a_dst >= a_dstEnd)
3200 return false;
3201 *a_dst++ = (SI_CHAR)a_cp;
3202 return true;
3203 }
3204 if (a_dst + 1 >= a_dstEnd)
3205 return false;
3206 a_cp -= 0x10000;
3207 *a_dst++ = (SI_CHAR)(0xD800 + (a_cp >> 10));
3208 *a_dst++ = (SI_CHAR)(0xDC00 + (a_cp & 0x3FF));
3209 return true;
3210 }
3211 return false;
3212}
3213
3214template <class SI_CHAR>
3215inline bool ReadCodePoint(const SI_CHAR *&a_src, char32_t &a_cp) {
3216 if (sizeof(SI_CHAR) >= sizeof(char32_t)) {
3217 a_cp = (char32_t)*a_src++;
3218 return true;
3219 }
3220 if (sizeof(SI_CHAR) == 2) {
3221 const char32_t w = (char32_t)*a_src++;
3222 if (w >= 0xD800 && w <= 0xDBFF) {
3223 if (*a_src >= 0xDC00 && *a_src <= 0xDFFF) {
3224 a_cp = 0x10000 + ((w - 0xD800) << 10) + ((char32_t)*a_src++ - 0xDC00);
3225 return true;
3226 }
3227 }
3228 a_cp = w;
3229 return true;
3230 }
3231 return false;
3232}
3233
3234} // namespace SI_UTF8
3235
3240template <class SI_CHAR> class SI_ConvertW {
3241 bool m_bStoreIsUtf8;
3242
3243protected:
3244 SI_ConvertW() {}
3245
3246public:
3247 SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) {}
3248
3249 /* copy and assignment */
3250 SI_ConvertW(const SI_ConvertW &rhs) { operator=(rhs); }
3251 SI_ConvertW &operator=(const SI_ConvertW &rhs) {
3252 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
3253 return *this;
3254 }
3255
3269 size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen) {
3270 SI_ASSERT(a_uInputDataLen != (size_t)-1);
3271
3272 if (m_bStoreIsUtf8) {
3273 // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
3274 // so we just return the same number of characters required as for
3275 // the source text.
3276 return a_uInputDataLen;
3277 }
3278
3279 // get the required buffer size
3280#if defined(_MSC_VER)
3281 size_t uBufSiz;
3282 errno_t e = mbstowcs_s(&uBufSiz, NULL, 0, a_pInputData, a_uInputDataLen);
3283 return (e == 0) ? uBufSiz : (size_t)-1;
3284#elif !defined(SI_NO_MBSTOWCS_NULL)
3285 // mbstowcs requires a_pInputData to be NUL-terminated at or after
3286 // a_pInputData + a_uInputDataLen. A length-only buffer without a
3287 // trailing NUL may read past the supplied range.
3288 return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
3289#else
3290 // fall back processing for platforms that don't support a NULL dest to mbstowcs
3291 // worst case scenario is 1:1, this will be a sufficient buffer size
3292 (void)a_pInputData;
3293 return a_uInputDataLen;
3294#endif
3295 }
3296
3310 bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen,
3311 SI_CHAR *a_pOutputData, size_t a_uOutputDataSize) {
3312 if (m_bStoreIsUtf8) {
3313 const char *src = a_pInputData;
3314 const char *srcEnd = a_pInputData + a_uInputDataLen;
3315 SI_CHAR *dst = a_pOutputData;
3316 SI_CHAR *dstEnd = a_pOutputData + a_uOutputDataSize;
3317 if (sizeof(SI_CHAR) != 2 && sizeof(SI_CHAR) < sizeof(char32_t)) {
3318 return false;
3319 }
3320 while (src < srcEnd) {
3321 char32_t cp;
3322 if (!SI_UTF8::Decode(src, srcEnd, cp)) {
3323 return false;
3324 }
3325 if (!SI_UTF8::AppendCodePoint(cp, dst, dstEnd)) {
3326 return false;
3327 }
3328 }
3329 return true;
3330 }
3331
3332 // convert to wchar_t
3333#if defined(_MSC_VER)
3334 size_t uBufSiz;
3335 errno_t e = mbstowcs_s(&uBufSiz, a_pOutputData, a_uOutputDataSize,
3336 a_pInputData, a_uInputDataLen);
3337 (void)uBufSiz;
3338 return (e == 0);
3339#else
3340 size_t retval = mbstowcs(a_pOutputData, a_pInputData, a_uOutputDataSize);
3341 return retval != (size_t)(-1);
3342#endif
3343 }
3344
3355 size_t SizeToStore(const SI_CHAR *a_pInputData) {
3356 if (m_bStoreIsUtf8) {
3357 // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
3358 size_t uLen = 0;
3359 while (a_pInputData[uLen]) {
3360 ++uLen;
3361 }
3362 return (6 * uLen) + 1;
3363 } else {
3364#if defined(_MSC_VER)
3365 size_t uLen = 0;
3366 errno_t e = wcstombs_s(&uLen, NULL, 0, a_pInputData, 0);
3367 if (e != 0) {
3368 return (size_t)-1;
3369 }
3370 return uLen + 1; // include NULL terminator
3371#else
3372 size_t uLen = wcstombs(NULL, a_pInputData, 0);
3373 if (uLen == (size_t)(-1)) {
3374 return uLen;
3375 }
3376 return uLen + 1; // include NULL terminator
3377#endif
3378 }
3379 }
3380
3394 bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData,
3395 size_t a_uOutputDataSize) {
3396 if (m_bStoreIsUtf8) {
3397 const SI_CHAR *src = a_pInputData;
3398 char *dst = a_pOutputData;
3399 char *dstEnd = a_pOutputData + a_uOutputDataSize;
3400 if (sizeof(SI_CHAR) != 2 && sizeof(SI_CHAR) < sizeof(char32_t)) {
3401 return false;
3402 }
3403 while (*src) {
3404 char32_t cp;
3405 if (!SI_UTF8::ReadCodePoint(src, cp)) {
3406 return false;
3407 }
3408 char buf[4];
3409 const int n = SI_UTF8::Encode(cp, buf, sizeof(buf));
3410 if (n < 0 || (size_t)(dstEnd - dst) < (size_t)n) {
3411 return false;
3412 }
3413 memcpy(dst, buf, (size_t)n);
3414 dst += n;
3415 }
3416 if (dst >= dstEnd) {
3417 return false;
3418 }
3419 *dst = '\0';
3420 return true;
3421 } else {
3422#if defined(_MSC_VER)
3423 size_t converted = 0;
3424 errno_t e = wcstombs_s(&converted, a_pOutputData, a_uOutputDataSize,
3425 a_pInputData, 0);
3426 return (e == 0);
3427#else
3428 size_t retval = wcstombs(a_pOutputData, a_pInputData, a_uOutputDataSize);
3429 return retval != (size_t)-1;
3430#endif
3431 }
3432 }
3433};
3434
3435#endif // SI_CONVERT_GENERIC
3436
3437// ---------------------------------------------------------------------------
3438// SI_CONVERT_ICU
3439// ---------------------------------------------------------------------------
3440#ifdef SI_CONVERT_ICU
3441
3442#define SI_Case SI_GenericCase
3443#define SI_NoCase SI_GenericNoCase
3444
3445#include <unicode/ucnv.h>
3446
3450template <class SI_CHAR> class SI_ConvertW {
3451 const char *m_pEncoding;
3452 UConverter *m_pConverter;
3453
3454protected:
3455 SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) {}
3456
3457public:
3458 SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
3459 m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
3460 }
3461
3462 /* copy and assignment */
3463 SI_ConvertW(const SI_ConvertW &rhs) { operator=(rhs); }
3464 SI_ConvertW &operator=(const SI_ConvertW &rhs) {
3465 m_pEncoding = rhs.m_pEncoding;
3466 m_pConverter = NULL;
3467 return *this;
3468 }
3469 ~SI_ConvertW() {
3470 if (m_pConverter)
3471 ucnv_close(m_pConverter);
3472 }
3473
3487 size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen) {
3488 SI_ASSERT(a_uInputDataLen != (size_t)-1);
3489
3490 UErrorCode nError;
3491
3492 if (!m_pConverter) {
3493 nError = U_ZERO_ERROR;
3494 m_pConverter = ucnv_open(m_pEncoding, &nError);
3495 if (U_FAILURE(nError)) {
3496 return (size_t)-1;
3497 }
3498 }
3499
3500 nError = U_ZERO_ERROR;
3501 int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0, a_pInputData,
3502 (int32_t)a_uInputDataLen, &nError);
3503 if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3504 return (size_t)-1;
3505 }
3506
3507 return (size_t)nLen;
3508 }
3509
3523 bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen,
3524 UChar *a_pOutputData, size_t a_uOutputDataSize) {
3525 UErrorCode nError;
3526
3527 if (!m_pConverter) {
3528 nError = U_ZERO_ERROR;
3529 m_pConverter = ucnv_open(m_pEncoding, &nError);
3530 if (U_FAILURE(nError)) {
3531 return false;
3532 }
3533 }
3534
3535 nError = U_ZERO_ERROR;
3536 ucnv_toUChars(m_pConverter, a_pOutputData, (int32_t)a_uOutputDataSize,
3537 a_pInputData, (int32_t)a_uInputDataLen, &nError);
3538 if (U_FAILURE(nError)) {
3539 return false;
3540 }
3541
3542 return true;
3543 }
3544
3555 size_t SizeToStore(const UChar *a_pInputData) {
3556 UErrorCode nError;
3557
3558 if (!m_pConverter) {
3559 nError = U_ZERO_ERROR;
3560 m_pConverter = ucnv_open(m_pEncoding, &nError);
3561 if (U_FAILURE(nError)) {
3562 return (size_t)-1;
3563 }
3564 }
3565
3566 nError = U_ZERO_ERROR;
3567 int32_t nLen =
3568 ucnv_fromUChars(m_pConverter, NULL, 0, a_pInputData, -1, &nError);
3569 if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3570 return (size_t)-1;
3571 }
3572
3573 return (size_t)nLen + 1;
3574 }
3575
3589 bool ConvertToStore(const UChar *a_pInputData, char *a_pOutputData,
3590 size_t a_uOutputDataSize) {
3591 UErrorCode nError;
3592
3593 if (!m_pConverter) {
3594 nError = U_ZERO_ERROR;
3595 m_pConverter = ucnv_open(m_pEncoding, &nError);
3596 if (U_FAILURE(nError)) {
3597 return false;
3598 }
3599 }
3600
3601 nError = U_ZERO_ERROR;
3602 ucnv_fromUChars(m_pConverter, a_pOutputData, (int32_t)a_uOutputDataSize,
3603 a_pInputData, -1, &nError);
3604 if (U_FAILURE(nError)) {
3605 return false;
3606 }
3607
3608 return true;
3609 }
3610};
3611
3612#endif // SI_CONVERT_ICU
3613
3614// ---------------------------------------------------------------------------
3615// SI_CONVERT_WIN32
3616// ---------------------------------------------------------------------------
3617#ifdef SI_CONVERT_WIN32
3618
3619#define SI_Case SI_GenericCase
3620
3621// Windows CE doesn't have errno or MBCS libraries
3622#ifdef _WIN32_WCE
3623#ifndef SI_NO_MBCS
3624#define SI_NO_MBCS
3625#endif
3626#endif
3627
3628#include <windows.h>
3629#ifdef SI_NO_MBCS
3630#define SI_NoCase SI_GenericNoCase
3631#else // !SI_NO_MBCS
3640#include <mbstring.h>
3641template <class SI_CHAR> struct SI_NoCase {
3642 bool operator()(const SI_CHAR *pLeft, const SI_CHAR *pRight) const {
3643 if (sizeof(SI_CHAR) == sizeof(char)) {
3644 return _mbsicmp((const unsigned char *)pLeft,
3645 (const unsigned char *)pRight) < 0;
3646 }
3647 if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
3648 return _wcsicmp((const wchar_t *)pLeft, (const wchar_t *)pRight) < 0;
3649 }
3651 }
3652};
3653#endif // SI_NO_MBCS
3654
3661template <class SI_CHAR> class SI_ConvertW {
3663
3664protected:
3665 SI_ConvertW() {}
3666
3667public:
3670 }
3671
3672 /* copy and assignment */
3673 SI_ConvertW(const SI_ConvertW &rhs) { operator=(rhs); }
3674 SI_ConvertW &operator=(const SI_ConvertW &rhs) {
3675 m_uCodePage = rhs.m_uCodePage;
3676 return *this;
3677 }
3678
3692 size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen) {
3693 SI_ASSERT(a_uInputDataLen != (size_t)-1);
3694
3696 (int)a_uInputDataLen, 0, 0);
3697 return (size_t)(retval > 0 ? retval : -1);
3698 }
3699
3713 bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen,
3715 int nSize =
3717 (wchar_t *)a_pOutputData, (int)a_uOutputDataSize);
3718 return (nSize > 0);
3719 }
3720
3731 size_t SizeToStore(const SI_CHAR *a_pInputData) {
3733 m_uCodePage, 0, (const wchar_t *)a_pInputData, -1, 0, 0, 0, 0);
3734 return (size_t)(retval > 0 ? retval : -1);
3735 }
3736
3750 bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData,
3751 size_t a_uOutputDataSize) {
3752 int retval =
3753 WideCharToMultiByte(m_uCodePage, 0, (const wchar_t *)a_pInputData, -1,
3754 a_pOutputData, (int)a_uOutputDataSize, 0, 0);
3755 return retval > 0;
3756 }
3757};
3758
3759#endif // SI_CONVERT_WIN32
3760
3761// ---------------------------------------------------------------------------
3762// SI_NO_CONVERSION
3763// ---------------------------------------------------------------------------
3764#ifdef SI_NO_CONVERSION
3765
3766#define SI_Case SI_GenericCase
3767#define SI_NoCase SI_GenericNoCase
3768
3769#endif // SI_NO_CONVERSION
3770
3771// ---------------------------------------------------------------------------
3772// TYPE DEFINITIONS
3773// ---------------------------------------------------------------------------
3774
3777
3778#if defined(SI_NO_CONVERSION)
3779// if there is no wide char conversion then we don't need to define the
3780// widechar "W" versions of CSimpleIni
3781#define CSimpleIni CSimpleIniA
3782#define CSimpleIniCase CSimpleIniCaseA
3783#define SI_NEWLINE SI_NEWLINE_A
3784#else
3785#if defined(SI_CONVERT_ICU)
3790#else
3795#endif
3796
3797#ifdef _UNICODE
3798#define CSimpleIni CSimpleIniW
3799#define CSimpleIniCase CSimpleIniCaseW
3800#define SI_NEWLINE SI_NEWLINE_W
3801#else // !_UNICODE
3802#define CSimpleIni CSimpleIniA
3803#define CSimpleIniCase CSimpleIniCaseA
3804#define SI_NEWLINE SI_NEWLINE_A
3805#endif // _UNICODE
3806#endif
3807
3808#ifdef _MSC_VER
3809#pragma warning(pop)
3810#endif
3811
3812#endif // INCLUDED_SimpleIni_h
Definition SimpleIni.h:448
Definition SimpleIni.h:405
Definition SimpleIni.h:393
Definition SimpleIni.h:418
Definition SimpleIni.h:331
bool DeleteValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pValue, bool a_bRemoveEmpty=false)
Definition SimpleIni.h:2824
bool GetAllKeys(const SI_CHAR *a_pSection, TNamesDepend &a_names) const
Definition SimpleIni.h:2541
int GetSectionSize(const SI_CHAR *a_pSection) const
Definition SimpleIni.h:2486
void SetAllowKeyOnly(bool a_bAllowKeyOnly=true)
Definition SimpleIni.h:592
SI_Error LoadData(const std::string &a_strData)
Definition SimpleIni.h:652
bool UsingQuotes() const
Definition SimpleIni.h:582
std::map< Entry, TKeyVal, typename Entry::KeyOrder > TSection
Definition SimpleIni.h:383
std::list< Entry > TNamesDepend
Definition SimpleIni.h:388
bool IsUnicode() const
Definition SimpleIni.h:521
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:2428
bool IsMultiKey() const
Definition SimpleIni.h:546
Converter GetConverter() const
Definition SimpleIni.h:1115
bool IsEmpty() const
Definition SimpleIni.h:496
std::multimap< Entry, const SI_CHAR *, typename Entry::KeyOrder > TKeyVal
Definition SimpleIni.h:380
const TKeyVal * GetSection(const SI_CHAR *a_pSection) const
Definition SimpleIni.h:2519
SI_Error Save(OutputWriter &a_oOutput, bool a_bAddSignature=false) const
Definition SimpleIni.h:2620
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:2236
bool GetBoolValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bDefault=false, bool *a_pHasMultiple=NULL) const
Definition SimpleIni.h:2390
long GetLongValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, long a_nDefault=0, bool *a_pHasMultiple=NULL) const
Definition SimpleIni.h:2268
void GetAllSections(TNamesDepend &a_names) const
Definition SimpleIni.h:2531
void Reset()
Definition SimpleIni.h:1280
~CSimpleIniTempl()
Definition SimpleIni.h:1275
bool UsingSpaces() const
Definition SimpleIni.h:571
SI_Error Save(std::string &a_sBuffer, bool a_bAddSignature=false) const
Definition SimpleIni.h:774
bool KeyExists(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey) const
Definition SimpleIni.h:869
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:978
bool SectionExists(const SI_CHAR *a_pSection) const
Definition SimpleIni.h:864
bool Delete(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bRemoveEmpty=false)
Definition SimpleIni.h:2818
void SetMultiLine(bool a_bAllowMultiLine=true)
Definition SimpleIni.h:555
void SetQuotes(bool a_bParseQuotes=true)
Definition SimpleIni.h:577
void SetUnicode(bool a_bIsUtf8=true)
Definition SimpleIni.h:515
double GetDoubleValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, double a_nDefault=0, bool *a_pHasMultiple=NULL) const
Definition SimpleIni.h:2333
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:2304
void SetMultiKey(bool a_bAllowMultiKey=true)
Definition SimpleIni.h:541
bool GetAllValues(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, TNamesDepend &a_values) const
Definition SimpleIni.h:2452
void SetSpaces(bool a_bSpaces=true)
Definition SimpleIni.h:568
SI_Error LoadFile(const char *a_pszFile)
Definition SimpleIni.h:1398
bool GetAllowKeyOnly() const
Definition SimpleIni.h:597
SI_Error SaveFile(const char *a_pszFile, bool a_bAddSignature=true) const
Definition SimpleIni.h:2568
CSimpleIniTempl(bool a_bIsUtf8=false, bool a_bMultiKey=false, bool a_bMultiLine=false)
Definition SimpleIni.h:1267
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:2361
bool IsMultiLine() const
Definition SimpleIni.h:560
Definition SimpleIni.h:2982
bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData, size_t a_uOutputDataSize)
Definition SimpleIni.h:3070
size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen)
Definition SimpleIni.h:3011
size_t SizeToStore(const SI_CHAR *a_pInputData)
Definition SimpleIni.h:3052
bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen, SI_CHAR *a_pOutputData, size_t a_uOutputDataSize)
Definition SimpleIni.h:3032
Definition SimpleIni.h:360
Definition SimpleIni.h:368
Definition SimpleIni.h:336
Definition SimpleIni.h:2944
Definition SimpleIni.h:2963