SimpleIni
SimpleIni.h
1 
211 #ifndef INCLUDED_SimpleIni_h
212 #define INCLUDED_SimpleIni_h
213 
214 #if defined(_MSC_VER) && (_MSC_VER >= 1020)
215 # pragma once
216 #endif
217 
218 // Disable these warnings in MSVC:
219 // 4127 "conditional expression is constant" as the conversion classes trigger
220 // it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
221 // be optimized away in a release build.
222 // 4503 'insert' : decorated name length exceeded, name was truncated
223 // 4702 "unreachable code" as the MS STL header causes it in release mode.
224 // Again, the code causing the warning will be cleaned up by the compiler.
225 // 4786 "identifier truncated to 256 characters" as this is thrown hundreds
226 // of times VC6 as soon as STL is used.
227 #ifdef _MSC_VER
228 # pragma warning (push)
229 # pragma warning (disable: 4127 4503 4702 4786)
230 #endif
231 
232 #include <cstring>
233 #include <cstdlib>
234 #include <string>
235 #include <map>
236 #include <list>
237 #include <algorithm>
238 #include <stdio.h>
239 
240 #ifdef SI_SUPPORT_IOSTREAMS
241 # include <iostream>
242 #endif // SI_SUPPORT_IOSTREAMS
243 
244 #ifdef _DEBUG
245 # ifndef assert
246 # include <cassert>
247 # endif
248 # define SI_ASSERT(x) assert(x)
249 #else
250 # define SI_ASSERT(x)
251 #endif
252 
253 using SI_Error = int;
254 
255 constexpr int SI_OK = 0;
256 constexpr int SI_UPDATED = 1;
257 constexpr int SI_INSERTED = 2;
258 
259 // note: test for any error with (retval < 0)
260 constexpr int SI_FAIL = -1;
261 constexpr int SI_NOMEM = -2;
262 constexpr int SI_FILE = -3;
263 
264 #define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
265 
266 #ifdef _WIN32
267 # define SI_NEWLINE_A "\r\n"
268 # define SI_NEWLINE_W L"\r\n"
269 #else // !_WIN32
270 # define SI_NEWLINE_A "\n"
271 # define SI_NEWLINE_W L"\n"
272 #endif // _WIN32
273 
274 #if defined(SI_CONVERT_ICU)
275 # include <unicode/ustring.h>
276 #endif
277 
278 #if defined(_WIN32)
279 # define SI_HAS_WIDE_FILE
280 # define SI_WCHAR_T wchar_t
281 #elif defined(SI_CONVERT_ICU)
282 # define SI_HAS_WIDE_FILE
283 # define SI_WCHAR_T UChar
284 #endif
285 
286 
287 // ---------------------------------------------------------------------------
288 // MAIN TEMPLATE CLASS
289 // ---------------------------------------------------------------------------
290 
310 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
312 {
313 public:
314  typedef SI_CHAR SI_CHAR_T;
315 
317  struct Entry {
318  const SI_CHAR * pItem;
319  const SI_CHAR * pComment;
320  int nOrder;
321 
322  Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0)
323  : pItem(a_pszItem)
324  , pComment(NULL)
325  , nOrder(a_nOrder)
326  { }
327  Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder)
328  : pItem(a_pszItem)
329  , pComment(a_pszComment)
330  , nOrder(a_nOrder)
331  { }
332  Entry(const Entry & rhs) { operator=(rhs); }
333  Entry & operator=(const Entry & rhs) {
334  pItem = rhs.pItem;
335  pComment = rhs.pComment;
336  nOrder = rhs.nOrder;
337  return *this;
338  }
339 
340 #if defined(_MSC_VER) && _MSC_VER <= 1200
342  bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); }
343  bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); }
344 #endif
345 
347  struct KeyOrder {
348  bool operator()(const Entry & lhs, const Entry & rhs) const {
349  const static SI_STRLESS isLess = SI_STRLESS();
350  return isLess(lhs.pItem, rhs.pItem);
351  }
352  };
353 
355  struct LoadOrder {
356  bool operator()(const Entry & lhs, const Entry & rhs) const {
357  if (lhs.nOrder != rhs.nOrder) {
358  return lhs.nOrder < rhs.nOrder;
359  }
360  return KeyOrder()(lhs.pItem, rhs.pItem);
361  }
362  };
363  };
364 
366  typedef std::multimap<Entry,const SI_CHAR *,typename Entry::KeyOrder> TKeyVal;
367 
369  typedef std::map<Entry,TKeyVal,typename Entry::KeyOrder> TSection;
370 
374  typedef std::list<Entry> TNamesDepend;
375 
379  class OutputWriter {
380  public:
381  OutputWriter() { }
382  virtual ~OutputWriter() { }
383  virtual void Write(const char * a_pBuf) = 0;
384  private:
385  OutputWriter(const OutputWriter &); // disable
386  OutputWriter & operator=(const OutputWriter &); // disable
387  };
388 
390  class FileWriter : public OutputWriter {
391  FILE * m_file;
392  public:
393  FileWriter(FILE * a_file) : m_file(a_file) { }
394  void Write(const char * a_pBuf) {
395  fputs(a_pBuf, m_file);
396  }
397  private:
398  FileWriter(const FileWriter &); // disable
399  FileWriter & operator=(const FileWriter &); // disable
400  };
401 
403  class StringWriter : public OutputWriter {
404  std::string & m_string;
405  public:
406  StringWriter(std::string & a_string) : m_string(a_string) { }
407  void Write(const char * a_pBuf) {
408  m_string.append(a_pBuf);
409  }
410  private:
411  StringWriter(const StringWriter &); // disable
412  StringWriter & operator=(const StringWriter &); // disable
413  };
414 
415 #ifdef SI_SUPPORT_IOSTREAMS
417  class StreamWriter : public OutputWriter {
418  std::ostream & m_ostream;
419  public:
420  StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { }
421  void Write(const char * a_pBuf) {
422  m_ostream << a_pBuf;
423  }
424  private:
425  StreamWriter(const StreamWriter &); // disable
426  StreamWriter & operator=(const StreamWriter &); // disable
427  };
428 #endif // SI_SUPPORT_IOSTREAMS
429 
433  class Converter : private SI_CONVERTER {
434  public:
435  Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) {
436  m_scratch.resize(1024);
437  }
438  Converter(const Converter & rhs) { operator=(rhs); }
439  Converter & operator=(const Converter & rhs) {
440  m_scratch = rhs.m_scratch;
441  return *this;
442  }
443  bool ConvertToStore(const SI_CHAR * a_pszString) {
444  size_t uLen = SI_CONVERTER::SizeToStore(a_pszString);
445  if (uLen == (size_t)(-1)) {
446  return false;
447  }
448  while (uLen > m_scratch.size()) {
449  m_scratch.resize(m_scratch.size() * 2);
450  }
451  return SI_CONVERTER::ConvertToStore(
452  a_pszString,
453  const_cast<char*>(m_scratch.data()),
454  m_scratch.size());
455  }
456  const char * Data() { return m_scratch.data(); }
457  private:
458  std::string m_scratch;
459  };
460 
461 public:
462  /*-----------------------------------------------------------------------*/
463 
471  bool a_bIsUtf8 = false,
472  bool a_bMultiKey = false,
473  bool a_bMultiLine = false
474  );
475 
478 
480  void Reset();
481 
483  bool IsEmpty() const { return m_data.empty(); }
484 
485  /*-----------------------------------------------------------------------*/
502  void SetUnicode(bool a_bIsUtf8 = true) {
503  if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8;
504  }
505 
507  bool IsUnicode() const { return m_bStoreIsUtf8; }
508 
527  void SetMultiKey(bool a_bAllowMultiKey = true) {
528  m_bAllowMultiKey = a_bAllowMultiKey;
529  }
530 
532  bool IsMultiKey() const { return m_bAllowMultiKey; }
533 
541  void SetMultiLine(bool a_bAllowMultiLine = true) {
542  m_bAllowMultiLine = a_bAllowMultiLine;
543  }
544 
546  bool IsMultiLine() const { return m_bAllowMultiLine; }
547 
554  void SetSpaces(bool a_bSpaces = true) {
555  m_bSpaces = a_bSpaces;
556  }
557 
559  bool UsingSpaces() const { return m_bSpaces; }
560 
561 
566  void SetQuotes(bool a_bParseQuotes = true) {
567  m_bParseQuotes = a_bParseQuotes;
568  }
569 
571  bool UsingQuotes() const { return m_bParseQuotes; }
572 
581  void SetAllowKeyOnly(bool a_bAllowKeyOnly = true) {
582  m_bAllowKeyOnly = a_bAllowKeyOnly;
583  }
584 
586  bool GetAllowKeyOnly() const { return m_bAllowKeyOnly; }
587 
588 
589 
590  /*-----------------------------------------------------------------------*/
602  SI_Error LoadFile(
603  const char * a_pszFile
604  );
605 
606 #ifdef SI_HAS_WIDE_FILE
613  SI_Error LoadFile(
614  const SI_WCHAR_T * a_pwszFile
615  );
616 #endif // SI_HAS_WIDE_FILE
617 
625  SI_Error LoadFile(
626  FILE * a_fpFile
627  );
628 
629 #ifdef SI_SUPPORT_IOSTREAMS
636  SI_Error LoadData(
637  std::istream & a_istream
638  );
639 #endif // SI_SUPPORT_IOSTREAMS
640 
647  SI_Error LoadData(const std::string & a_strData) {
648  return LoadData(a_strData.c_str(), a_strData.size());
649  }
650 
658  SI_Error LoadData(
659  const char * a_pData,
660  size_t a_uDataLen
661  );
662 
663  /*-----------------------------------------------------------------------*/
679  SI_Error SaveFile(
680  const char * a_pszFile,
681  bool a_bAddSignature = true
682  ) const;
683 
684 #ifdef SI_HAS_WIDE_FILE
695  SI_Error SaveFile(
696  const SI_WCHAR_T * a_pwszFile,
697  bool a_bAddSignature = true
698  ) const;
699 #endif // _WIN32
700 
713  SI_Error SaveFile(
714  FILE * a_pFile,
715  bool a_bAddSignature = false
716  ) const;
717 
749  SI_Error Save(
750  OutputWriter & a_oOutput,
751  bool a_bAddSignature = false
752  ) const;
753 
754 #ifdef SI_SUPPORT_IOSTREAMS
766  SI_Error Save(
767  std::ostream & a_ostream,
768  bool a_bAddSignature = false
769  ) const
770  {
771  StreamWriter writer(a_ostream);
772  return Save(writer, a_bAddSignature);
773  }
774 #endif // SI_SUPPORT_IOSTREAMS
775 
787  SI_Error Save(
788  std::string & a_sBuffer,
789  bool a_bAddSignature = false
790  ) const
791  {
792  StringWriter writer(a_sBuffer);
793  return Save(writer, a_bAddSignature);
794  }
795 
796  /*-----------------------------------------------------------------------*/
814  void GetAllSections(
815  TNamesDepend & a_names
816  ) const;
817 
835  bool GetAllKeys(
836  const SI_CHAR * a_pSection,
837  TNamesDepend & a_names
838  ) const;
839 
856  bool GetAllValues(
857  const SI_CHAR * a_pSection,
858  const SI_CHAR * a_pKey,
859  TNamesDepend & a_values
860  ) const;
861 
871  int GetSectionSize(
872  const SI_CHAR * a_pSection
873  ) const;
874 
888  const TKeyVal * GetSection(
889  const SI_CHAR * a_pSection
890  ) const;
891 
893  inline bool SectionExists(
894  const SI_CHAR * a_pSection
895  ) const {
896  return GetSection(a_pSection) != NULL;
897  }
898 
900  inline bool KeyExists(
901  const SI_CHAR * a_pSection,
902  const SI_CHAR * a_pKey
903  ) const {
904  return GetValue(a_pSection, a_pKey) != NULL;
905  }
906 
924  const SI_CHAR * GetValue(
925  const SI_CHAR * a_pSection,
926  const SI_CHAR * a_pKey,
927  const SI_CHAR * a_pDefault = NULL,
928  bool * a_pHasMultiple = NULL
929  ) const;
930 
944  long GetLongValue(
945  const SI_CHAR * a_pSection,
946  const SI_CHAR * a_pKey,
947  long a_nDefault = 0,
948  bool * a_pHasMultiple = NULL
949  ) const;
950 
964  double GetDoubleValue(
965  const SI_CHAR * a_pSection,
966  const SI_CHAR * a_pKey,
967  double a_nDefault = 0,
968  bool * a_pHasMultiple = NULL
969  ) const;
970 
989  bool GetBoolValue(
990  const SI_CHAR * a_pSection,
991  const SI_CHAR * a_pKey,
992  bool a_bDefault = false,
993  bool * a_pHasMultiple = NULL
994  ) const;
995 
1025  SI_Error SetValue(
1026  const SI_CHAR * a_pSection,
1027  const SI_CHAR * a_pKey,
1028  const SI_CHAR * a_pValue,
1029  const SI_CHAR * a_pComment = NULL,
1030  bool a_bForceReplace = false
1031  )
1032  {
1033  return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true);
1034  }
1035 
1059  SI_Error SetLongValue(
1060  const SI_CHAR * a_pSection,
1061  const SI_CHAR * a_pKey,
1062  long a_nValue,
1063  const SI_CHAR * a_pComment = NULL,
1064  bool a_bUseHex = false,
1065  bool a_bForceReplace = false
1066  );
1067 
1088  SI_Error SetDoubleValue(
1089  const SI_CHAR * a_pSection,
1090  const SI_CHAR * a_pKey,
1091  double a_nValue,
1092  const SI_CHAR * a_pComment = NULL,
1093  bool a_bForceReplace = false
1094  );
1095 
1116  SI_Error SetBoolValue(
1117  const SI_CHAR * a_pSection,
1118  const SI_CHAR * a_pKey,
1119  bool a_bValue,
1120  const SI_CHAR * a_pComment = NULL,
1121  bool a_bForceReplace = false
1122  );
1123 
1142  bool Delete(
1143  const SI_CHAR * a_pSection,
1144  const SI_CHAR * a_pKey,
1145  bool a_bRemoveEmpty = false
1146  );
1147 
1168  bool DeleteValue(
1169  const SI_CHAR * a_pSection,
1170  const SI_CHAR * a_pKey,
1171  const SI_CHAR * a_pValue,
1172  bool a_bRemoveEmpty = false
1173  );
1174 
1175  /*-----------------------------------------------------------------------*/
1185  return Converter(m_bStoreIsUtf8);
1186  }
1187 
1188  /*-----------------------------------------------------------------------*/
1191 private:
1192  // copying is not permitted
1193  CSimpleIniTempl(const CSimpleIniTempl &); // disabled
1194  CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled
1195 
1198  SI_Error FindFileComment(
1199  SI_CHAR *& a_pData,
1200  bool a_bCopyStrings
1201  );
1202 
1207  bool FindEntry(
1208  SI_CHAR *& a_pData,
1209  const SI_CHAR *& a_pSection,
1210  const SI_CHAR *& a_pKey,
1211  const SI_CHAR *& a_pVal,
1212  const SI_CHAR *& a_pComment
1213  ) const;
1214 
1237  SI_Error AddEntry(
1238  const SI_CHAR * a_pSection,
1239  const SI_CHAR * a_pKey,
1240  const SI_CHAR * a_pValue,
1241  const SI_CHAR * a_pComment,
1242  bool a_bForceReplace,
1243  bool a_bCopyStrings
1244  );
1245 
1247  inline bool IsSpace(SI_CHAR ch) const {
1248  return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
1249  }
1250 
1252  inline bool IsComment(SI_CHAR ch) const {
1253  return (ch == ';' || ch == '#');
1254  }
1255 
1256 
1258  inline void SkipNewLine(SI_CHAR *& a_pData) const {
1259  a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1;
1260  }
1261 
1263  SI_Error CopyString(const SI_CHAR *& a_pString);
1264 
1266  void DeleteString(const SI_CHAR * a_pString);
1267 
1269  bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const {
1270  const static SI_STRLESS isLess = SI_STRLESS();
1271  return isLess(a_pLeft, a_pRight);
1272  }
1273 
1274  bool IsMultiLineTag(const SI_CHAR * a_pData) const;
1275  bool IsMultiLineData(const SI_CHAR * a_pData) const;
1276  bool IsSingleLineQuotedValue(const SI_CHAR* a_pData) const;
1277  bool LoadMultiLineText(
1278  SI_CHAR *& a_pData,
1279  const SI_CHAR *& a_pVal,
1280  const SI_CHAR * a_pTagName,
1281  bool a_bAllowBlankLinesInComment = false
1282  ) const;
1283  bool IsNewLineChar(SI_CHAR a_c) const;
1284 
1285  bool OutputMultiLineText(
1286  OutputWriter & a_oOutput,
1287  Converter & a_oConverter,
1288  const SI_CHAR * a_pText
1289  ) const;
1290 
1291 private:
1297  SI_CHAR * m_pData;
1298 
1303  size_t m_uDataLen;
1304 
1306  const SI_CHAR * m_pFileComment;
1307 
1309  const SI_CHAR m_cEmptyString;
1310 
1312  TSection m_data;
1313 
1318  TNamesDepend m_strings;
1319 
1321  bool m_bStoreIsUtf8;
1322 
1324  bool m_bAllowMultiKey;
1325 
1327  bool m_bAllowMultiLine;
1328 
1330  bool m_bSpaces;
1331 
1333  bool m_bParseQuotes;
1334 
1336  bool m_bAllowKeyOnly;
1337 
1341  int m_nOrder;
1342 };
1343 
1344 // ---------------------------------------------------------------------------
1345 // IMPLEMENTATION
1346 // ---------------------------------------------------------------------------
1347 
1348 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1350  bool a_bIsUtf8,
1351  bool a_bAllowMultiKey,
1352  bool a_bAllowMultiLine
1353  )
1354  : m_pData(0)
1355  , m_uDataLen(0)
1356  , m_pFileComment(NULL)
1357  , m_cEmptyString(0)
1358  , m_bStoreIsUtf8(a_bIsUtf8)
1359  , m_bAllowMultiKey(a_bAllowMultiKey)
1360  , m_bAllowMultiLine(a_bAllowMultiLine)
1361  , m_bSpaces(true)
1362  , m_bParseQuotes(false)
1363  , m_bAllowKeyOnly(false)
1364  , m_nOrder(0)
1365 { }
1366 
1367 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1369 {
1370  Reset();
1371 }
1372 
1373 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1374 void
1376 {
1377  // remove all data
1378  delete[] m_pData;
1379  m_pData = NULL;
1380  m_uDataLen = 0;
1381  m_pFileComment = NULL;
1382  if (!m_data.empty()) {
1383  m_data.erase(m_data.begin(), m_data.end());
1384  }
1385 
1386  // remove all strings
1387  if (!m_strings.empty()) {
1388  typename TNamesDepend::iterator i = m_strings.begin();
1389  for (; i != m_strings.end(); ++i) {
1390  delete[] const_cast<SI_CHAR*>(i->pItem);
1391  }
1392  m_strings.erase(m_strings.begin(), m_strings.end());
1393  }
1394 }
1395 
1396 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1397 SI_Error
1399  const char * a_pszFile
1400  )
1401 {
1402  FILE * fp = NULL;
1403 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1404  fopen_s(&fp, a_pszFile, "rb");
1405 #else // !__STDC_WANT_SECURE_LIB__
1406  fp = fopen(a_pszFile, "rb");
1407 #endif // __STDC_WANT_SECURE_LIB__
1408  if (!fp) {
1409  return SI_FILE;
1410  }
1411  SI_Error rc = LoadFile(fp);
1412  fclose(fp);
1413  return rc;
1414 }
1415 
1416 #ifdef SI_HAS_WIDE_FILE
1417 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1418 SI_Error
1420  const SI_WCHAR_T * a_pwszFile
1421  )
1422 {
1423 #ifdef _WIN32
1424  FILE * fp = NULL;
1425 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1426  _wfopen_s(&fp, a_pwszFile, L"rb");
1427 #else // !__STDC_WANT_SECURE_LIB__
1428  fp = _wfopen(a_pwszFile, L"rb");
1429 #endif // __STDC_WANT_SECURE_LIB__
1430  if (!fp) return SI_FILE;
1431  SI_Error rc = LoadFile(fp);
1432  fclose(fp);
1433  return rc;
1434 #else // !_WIN32 (therefore SI_CONVERT_ICU)
1435  char szFile[256];
1436  u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
1437  return LoadFile(szFile);
1438 #endif // _WIN32
1439 }
1440 #endif // SI_HAS_WIDE_FILE
1441 
1442 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1443 SI_Error
1445  FILE * a_fpFile
1446  )
1447 {
1448  // load the raw file data
1449  int retval = fseek(a_fpFile, 0, SEEK_END);
1450  if (retval != 0) {
1451  return SI_FILE;
1452  }
1453  long lSize = ftell(a_fpFile);
1454  if (lSize < 0) {
1455  return SI_FILE;
1456  }
1457  if (lSize == 0) {
1458  return SI_OK;
1459  }
1460 
1461  // allocate and ensure NULL terminated
1462  char * pData = new(std::nothrow) char[lSize+static_cast<size_t>(1)];
1463  if (!pData) {
1464  return SI_NOMEM;
1465  }
1466  pData[lSize] = 0;
1467 
1468  // load data into buffer
1469  fseek(a_fpFile, 0, SEEK_SET);
1470  size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
1471  if (uRead != (size_t) lSize) {
1472  delete[] pData;
1473  return SI_FILE;
1474  }
1475 
1476  // convert the raw data to unicode
1477  SI_Error rc = LoadData(pData, uRead);
1478  delete[] pData;
1479  return rc;
1480 }
1481 
1482 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1483 SI_Error
1485  const char * a_pData,
1486  size_t a_uDataLen
1487  )
1488 {
1489  if (!a_pData) {
1490  return SI_OK;
1491  }
1492 
1493  // if the UTF-8 BOM exists, consume it and set mode to unicode, if we have
1494  // already loaded data and try to change mode half-way through then this will
1495  // be ignored and we will assert in debug versions
1496  if (a_uDataLen >= 3 && memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
1497  a_pData += 3;
1498  a_uDataLen -= 3;
1499  SI_ASSERT(m_bStoreIsUtf8 || !m_pData); // we don't expect mixed mode data
1500  SetUnicode();
1501  }
1502 
1503  if (a_uDataLen == 0) {
1504  return SI_OK;
1505  }
1506 
1507  // determine the length of the converted data
1508  SI_CONVERTER converter(m_bStoreIsUtf8);
1509  size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
1510  if (uLen == (size_t)(-1)) {
1511  return SI_FAIL;
1512  }
1513 
1514  // allocate memory for the data, ensure that there is a NULL
1515  // terminator wherever the converted data ends
1516  SI_CHAR * pData = new(std::nothrow) SI_CHAR[uLen+1];
1517  if (!pData) {
1518  return SI_NOMEM;
1519  }
1520  memset(pData, 0, sizeof(SI_CHAR)*(uLen+1));
1521 
1522  // convert the data
1523  if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
1524  delete[] pData;
1525  return SI_FAIL;
1526  }
1527 
1528  // parse it
1529  const static SI_CHAR empty = 0;
1530  SI_CHAR * pWork = pData;
1531  const SI_CHAR * pSection = &empty;
1532  const SI_CHAR * pItem = NULL;
1533  const SI_CHAR * pVal = NULL;
1534  const SI_CHAR * pComment = NULL;
1535 
1536  // We copy the strings if we are loading data into this class when we
1537  // already have stored some.
1538  bool bCopyStrings = (m_pData != NULL);
1539 
1540  // find a file comment if it exists, this is a comment that starts at the
1541  // beginning of the file and continues until the first blank line.
1542  SI_Error rc = FindFileComment(pWork, bCopyStrings);
1543  if (rc < 0) return rc;
1544 
1545  // add every entry in the file to the data table
1546  while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
1547  rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings);
1548  if (rc < 0) return rc;
1549  }
1550 
1551  // store these strings if we didn't copy them
1552  if (bCopyStrings) {
1553  delete[] pData;
1554  }
1555  else {
1556  m_pData = pData;
1557  m_uDataLen = uLen+1;
1558  }
1559 
1560  return SI_OK;
1561 }
1562 
1563 #ifdef SI_SUPPORT_IOSTREAMS
1564 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1565 SI_Error
1567  std::istream & a_istream
1568  )
1569 {
1570  std::string strData;
1571  char szBuf[512];
1572  do {
1573  a_istream.get(szBuf, sizeof(szBuf), '\0');
1574  strData.append(szBuf);
1575  }
1576  while (a_istream.good());
1577  return LoadData(strData);
1578 }
1579 #endif // SI_SUPPORT_IOSTREAMS
1580 
1581 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1582 SI_Error
1584  SI_CHAR *& a_pData,
1585  bool a_bCopyStrings
1586  )
1587 {
1588  // there can only be a single file comment
1589  if (m_pFileComment) {
1590  return SI_OK;
1591  }
1592 
1593  // Load the file comment as multi-line text, this will modify all of
1594  // the newline characters to be single \n chars
1595  if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
1596  return SI_OK;
1597  }
1598 
1599  // copy the string if necessary
1600  if (a_bCopyStrings) {
1601  SI_Error rc = CopyString(m_pFileComment);
1602  if (rc < 0) return rc;
1603  }
1604 
1605  return SI_OK;
1606 }
1607 
1608 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1609 bool
1611  SI_CHAR *& a_pData,
1612  const SI_CHAR *& a_pSection,
1613  const SI_CHAR *& a_pKey,
1614  const SI_CHAR *& a_pVal,
1615  const SI_CHAR *& a_pComment
1616  ) const
1617 {
1618  a_pComment = NULL;
1619 
1620  bool bHaveValue = false;
1621  SI_CHAR * pTrail = NULL;
1622  while (*a_pData) {
1623  // skip spaces and empty lines
1624  while (*a_pData && IsSpace(*a_pData)) {
1625  ++a_pData;
1626  }
1627  if (!*a_pData) {
1628  break;
1629  }
1630 
1631  // skip processing of comment lines but keep a pointer to
1632  // the start of the comment.
1633  if (IsComment(*a_pData)) {
1634  LoadMultiLineText(a_pData, a_pComment, NULL, true);
1635  continue;
1636  }
1637 
1638  // process section names
1639  if (*a_pData == '[') {
1640  // skip leading spaces
1641  ++a_pData;
1642  while (*a_pData && IsSpace(*a_pData)) {
1643  ++a_pData;
1644  }
1645 
1646  // find the end of the section name (it may contain spaces)
1647  // and convert it to lowercase as necessary
1648  a_pSection = a_pData;
1649  while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
1650  ++a_pData;
1651  }
1652 
1653  // if it's an invalid line, just skip it
1654  if (*a_pData != ']') {
1655  continue;
1656  }
1657 
1658  // remove trailing spaces from the section
1659  pTrail = a_pData - 1;
1660  while (pTrail >= a_pSection && IsSpace(*pTrail)) {
1661  --pTrail;
1662  }
1663  ++pTrail;
1664  *pTrail = 0;
1665 
1666  // skip to the end of the line
1667  ++a_pData; // safe as checked that it == ']' above
1668  while (*a_pData && !IsNewLineChar(*a_pData)) {
1669  ++a_pData;
1670  }
1671 
1672  a_pKey = NULL;
1673  a_pVal = NULL;
1674  return true;
1675  }
1676 
1677  // find the end of the key name (it may contain spaces)
1678  a_pKey = a_pData;
1679  while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
1680  ++a_pData;
1681  }
1682  // *a_pData is null, equals, or newline
1683 
1684  // if no value and we don't allow no value, then invalid
1685  bHaveValue = (*a_pData == '=');
1686  if (!bHaveValue && !m_bAllowKeyOnly) {
1687  continue;
1688  }
1689 
1690  // empty keys are invalid
1691  if (bHaveValue && a_pKey == a_pData) {
1692  while (*a_pData && !IsNewLineChar(*a_pData)) {
1693  ++a_pData;
1694  }
1695  continue;
1696  }
1697 
1698  // remove trailing spaces from the key
1699  pTrail = a_pData - 1;
1700  while (pTrail >= a_pKey && IsSpace(*pTrail)) {
1701  --pTrail;
1702  }
1703  ++pTrail;
1704 
1705  if (bHaveValue) {
1706  // process the value
1707  *pTrail = 0;
1708 
1709  // skip leading whitespace on the value
1710  ++a_pData; // safe as checked that it == '=' above
1711  while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
1712  ++a_pData;
1713  }
1714 
1715  // find the end of the value which is the end of this line
1716  a_pVal = a_pData;
1717  while (*a_pData && !IsNewLineChar(*a_pData)) {
1718  ++a_pData;
1719  }
1720 
1721  // remove trailing spaces from the value
1722  pTrail = a_pData - 1;
1723  if (*a_pData) { // prepare for the next round
1724  SkipNewLine(a_pData);
1725  }
1726  while (pTrail >= a_pVal && IsSpace(*pTrail)) {
1727  --pTrail;
1728  }
1729  ++pTrail;
1730  *pTrail = 0;
1731 
1732  // check for multi-line entries
1733  if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
1734  // skip the "<<<" to get the tag that will end the multiline
1735  const SI_CHAR* pTagName = a_pVal + 3;
1736  return LoadMultiLineText(a_pData, a_pVal, pTagName);
1737  }
1738 
1739  // check for quoted values, we are not supporting escapes in quoted values (yet)
1740  if (m_bParseQuotes) {
1741  --pTrail;
1742  if (pTrail > a_pVal && *a_pVal == '"' && *pTrail == '"') {
1743  ++a_pVal;
1744  *pTrail = 0;
1745  }
1746  }
1747  }
1748  else {
1749  // no value to process, just prepare for the next
1750  if (*a_pData) {
1751  SkipNewLine(a_pData);
1752  }
1753  *pTrail = 0;
1754  }
1755 
1756  // return the standard entry
1757  return true;
1758  }
1759 
1760  return false;
1761 }
1762 
1763 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1764 bool
1766  const SI_CHAR * a_pVal
1767  ) const
1768 {
1769  // check for the "<<<" prefix for a multi-line entry
1770  if (*a_pVal++ != '<') return false;
1771  if (*a_pVal++ != '<') return false;
1772  if (*a_pVal++ != '<') return false;
1773  return true;
1774 }
1775 
1776 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1777 bool
1779  const SI_CHAR * a_pData
1780  ) const
1781 {
1782  // data is multi-line if it has any of the following features:
1783  // * whitespace prefix
1784  // * embedded newlines
1785  // * whitespace suffix
1786 
1787  // empty string
1788  if (!*a_pData) {
1789  return false;
1790  }
1791 
1792  // check for prefix
1793  if (IsSpace(*a_pData)) {
1794  return true;
1795  }
1796 
1797  // embedded newlines
1798  while (*a_pData) {
1799  if (IsNewLineChar(*a_pData)) {
1800  return true;
1801  }
1802  ++a_pData;
1803  }
1804 
1805  // check for suffix
1806  if (IsSpace(*--a_pData)) {
1807  return true;
1808  }
1809 
1810  return false;
1811 }
1812 
1813 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1814 bool
1816  const SI_CHAR* a_pData
1817 ) const
1818 {
1819  // data needs quoting if it starts or ends with whitespace
1820  // and doesn't have embedded newlines
1821 
1822  // empty string
1823  if (!*a_pData) {
1824  return false;
1825  }
1826 
1827  // check for prefix
1828  if (IsSpace(*a_pData)) {
1829  return true;
1830  }
1831 
1832  // embedded newlines
1833  while (*a_pData) {
1834  if (IsNewLineChar(*a_pData)) {
1835  return false;
1836  }
1837  ++a_pData;
1838  }
1839 
1840  // check for suffix
1841  if (IsSpace(*--a_pData)) {
1842  return true;
1843  }
1844 
1845  return false;
1846 }
1847 
1848 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1849 bool
1851  SI_CHAR a_c
1852  ) const
1853 {
1854  return (a_c == '\n' || a_c == '\r');
1855 }
1856 
1857 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1858 bool
1860  SI_CHAR *& a_pData,
1861  const SI_CHAR *& a_pVal,
1862  const SI_CHAR * a_pTagName,
1863  bool a_bAllowBlankLinesInComment
1864  ) const
1865 {
1866  // we modify this data to strip all newlines down to a single '\n'
1867  // character. This means that on Windows we need to strip out some
1868  // characters which will make the data shorter.
1869  // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
1870  // LINE1-LINE1\nLINE2-LINE2\0
1871  // The pDataLine entry is the pointer to the location in memory that
1872  // the current line needs to start to run following the existing one.
1873  // This may be the same as pCurrLine in which case no move is needed.
1874  SI_CHAR * pDataLine = a_pData;
1875  SI_CHAR * pCurrLine;
1876 
1877  // value starts at the current line
1878  a_pVal = a_pData;
1879 
1880  // find the end tag. This tag must start in column 1 and be
1881  // followed by a newline. We ignore any whitespace after the end
1882  // tag but not whitespace before it.
1883  SI_CHAR cEndOfLineChar = *a_pData;
1884  for(;;) {
1885  // if we are loading comments then we need a comment character as
1886  // the first character on every line
1887  if (!a_pTagName && !IsComment(*a_pData)) {
1888  // if we aren't allowing blank lines then we're done
1889  if (!a_bAllowBlankLinesInComment) {
1890  break;
1891  }
1892 
1893  // if we are allowing blank lines then we only include them
1894  // in this comment if another comment follows, so read ahead
1895  // to find out.
1896  SI_CHAR * pCurr = a_pData;
1897  int nNewLines = 0;
1898  while (IsSpace(*pCurr)) {
1899  if (IsNewLineChar(*pCurr)) {
1900  ++nNewLines;
1901  SkipNewLine(pCurr);
1902  }
1903  else {
1904  ++pCurr;
1905  }
1906  }
1907 
1908  // we have a comment, add the blank lines to the output
1909  // and continue processing from here
1910  if (IsComment(*pCurr)) {
1911  for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n';
1912  a_pData = pCurr;
1913  continue;
1914  }
1915 
1916  // the comment ends here
1917  break;
1918  }
1919 
1920  // find the end of this line
1921  pCurrLine = a_pData;
1922  while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
1923 
1924  // move this line down to the location that it should be if necessary
1925  if (pDataLine < pCurrLine) {
1926  size_t nLen = (size_t) (a_pData - pCurrLine);
1927  memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
1928  pDataLine[nLen] = '\0';
1929  }
1930 
1931  // end the line with a NULL
1932  cEndOfLineChar = *a_pData;
1933  *a_pData = 0;
1934 
1935  // if are looking for a tag then do the check now. This is done before
1936  // checking for end of the data, so that if we have the tag at the end
1937  // of the data then the tag is removed correctly.
1938  if (a_pTagName) {
1939  // strip whitespace from the end of this tag
1940  SI_CHAR* pc = a_pData - 1;
1941  while (pc > pDataLine && IsSpace(*pc)) --pc;
1942  SI_CHAR ch = *++pc;
1943  *pc = 0;
1944 
1945  if (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)) {
1946  break;
1947  }
1948 
1949  *pc = ch;
1950  }
1951 
1952  // if we are at the end of the data then we just automatically end
1953  // this entry and return the current data.
1954  if (!cEndOfLineChar) {
1955  return true;
1956  }
1957 
1958  // otherwise we need to process this newline to ensure that it consists
1959  // of just a single \n character.
1960  pDataLine += (a_pData - pCurrLine);
1961  *a_pData = cEndOfLineChar;
1962  SkipNewLine(a_pData);
1963  *pDataLine++ = '\n';
1964  }
1965 
1966  // if we didn't find a comment at all then return false
1967  if (a_pVal == a_pData) {
1968  a_pVal = NULL;
1969  return false;
1970  }
1971 
1972  // the data (which ends at the end of the last line) needs to be
1973  // null-terminated BEFORE before the newline character(s). If the
1974  // user wants a new line in the multi-line data then they need to
1975  // add an empty line before the tag.
1976  *--pDataLine = '\0';
1977 
1978  // if looking for a tag and if we aren't at the end of the data,
1979  // then move a_pData to the start of the next line.
1980  if (a_pTagName && cEndOfLineChar) {
1981  SI_ASSERT(IsNewLineChar(cEndOfLineChar));
1982  *a_pData = cEndOfLineChar;
1983  SkipNewLine(a_pData);
1984  }
1985 
1986  return true;
1987 }
1988 
1989 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1990 SI_Error
1992  const SI_CHAR *& a_pString
1993  )
1994 {
1995  size_t uLen = 0;
1996  if (sizeof(SI_CHAR) == sizeof(char)) {
1997  uLen = strlen((const char *)a_pString);
1998  }
1999  else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
2000  uLen = wcslen((const wchar_t *)a_pString);
2001  }
2002  else {
2003  for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
2004  }
2005  ++uLen; // NULL character
2006  SI_CHAR * pCopy = new(std::nothrow) SI_CHAR[uLen];
2007  if (!pCopy) {
2008  return SI_NOMEM;
2009  }
2010  memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
2011  m_strings.push_back(pCopy);
2012  a_pString = pCopy;
2013  return SI_OK;
2014 }
2015 
2016 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2017 SI_Error
2019  const SI_CHAR * a_pSection,
2020  const SI_CHAR * a_pKey,
2021  const SI_CHAR * a_pValue,
2022  const SI_CHAR * a_pComment,
2023  bool a_bForceReplace,
2024  bool a_bCopyStrings
2025  )
2026 {
2027  SI_Error rc;
2028  bool bInserted = false;
2029 
2030  SI_ASSERT(!a_pComment || IsComment(*a_pComment));
2031 
2032  // if we are copying strings then make a copy of the comment now
2033  // because we will need it when we add the entry.
2034  if (a_bCopyStrings && a_pComment) {
2035  rc = CopyString(a_pComment);
2036  if (rc < 0) return rc;
2037  }
2038 
2039  // create the section entry if necessary
2040  typename TSection::iterator iSection = m_data.find(a_pSection);
2041  if (iSection == m_data.end()) {
2042  // if the section doesn't exist then we need a copy as the
2043  // string needs to last beyond the end of this function
2044  if (a_bCopyStrings) {
2045  rc = CopyString(a_pSection);
2046  if (rc < 0) return rc;
2047  }
2048 
2049  // only set the comment if this is a section only entry
2050  Entry oSection(a_pSection, ++m_nOrder);
2051  if (a_pComment && !a_pKey) {
2052  oSection.pComment = a_pComment;
2053  }
2054 
2055  typename TSection::value_type oEntry(oSection, TKeyVal());
2056  typedef typename TSection::iterator SectionIterator;
2057  std::pair<SectionIterator,bool> i = m_data.insert(oEntry);
2058  iSection = i.first;
2059  bInserted = true;
2060  }
2061  if (!a_pKey) {
2062  // section only entries are specified with pItem as NULL
2063  return bInserted ? SI_INSERTED : SI_UPDATED;
2064  }
2065 
2066  // check for existence of the key
2067  TKeyVal & keyval = iSection->second;
2068  typename TKeyVal::iterator iKey = keyval.find(a_pKey);
2069  bInserted = iKey == keyval.end();
2070 
2071  // remove all existing entries but save the load order and
2072  // comment of the first entry
2073  int nLoadOrder = ++m_nOrder;
2074  if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) {
2075  const SI_CHAR * pComment = NULL;
2076  while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) {
2077  if (iKey->first.nOrder < nLoadOrder) {
2078  nLoadOrder = iKey->first.nOrder;
2079  pComment = iKey->first.pComment;
2080  }
2081  ++iKey;
2082  }
2083  if (pComment) {
2084  DeleteString(a_pComment);
2085  a_pComment = pComment;
2086  CopyString(a_pComment);
2087  }
2088  Delete(a_pSection, a_pKey);
2089  iKey = keyval.end();
2090  }
2091 
2092  // values need to be a valid string, even if they are an empty string
2093  if (!a_pValue) {
2094  a_pValue = &m_cEmptyString;
2095  }
2096 
2097  // make string copies if necessary
2098  bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
2099  if (a_bCopyStrings) {
2100  if (bForceCreateNewKey || iKey == keyval.end()) {
2101  // if the key doesn't exist then we need a copy as the
2102  // string needs to last beyond the end of this function
2103  // because we will be inserting the key next
2104  rc = CopyString(a_pKey);
2105  if (rc < 0) return rc;
2106  }
2107 
2108  // we always need a copy of the value
2109  rc = CopyString(a_pValue);
2110  if (rc < 0) return rc;
2111  }
2112 
2113  // create the key entry
2114  if (iKey == keyval.end() || bForceCreateNewKey) {
2115  Entry oKey(a_pKey, nLoadOrder);
2116  if (a_pComment) {
2117  oKey.pComment = a_pComment;
2118  }
2119  typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL));
2120  iKey = keyval.insert(oEntry);
2121  }
2122 
2123  iKey->second = a_pValue;
2124  return bInserted ? SI_INSERTED : SI_UPDATED;
2125 }
2126 
2127 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2128 const SI_CHAR *
2130  const SI_CHAR * a_pSection,
2131  const SI_CHAR * a_pKey,
2132  const SI_CHAR * a_pDefault,
2133  bool * a_pHasMultiple
2134  ) const
2135 {
2136  if (a_pHasMultiple) {
2137  *a_pHasMultiple = false;
2138  }
2139  if (!a_pSection || !a_pKey) {
2140  return a_pDefault;
2141  }
2142  typename TSection::const_iterator iSection = m_data.find(a_pSection);
2143  if (iSection == m_data.end()) {
2144  return a_pDefault;
2145  }
2146  typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2147  if (iKeyVal == iSection->second.end()) {
2148  return a_pDefault;
2149  }
2150 
2151  // check for multiple entries with the same key
2152  if (m_bAllowMultiKey && a_pHasMultiple) {
2153  typename TKeyVal::const_iterator iTemp = iKeyVal;
2154  if (++iTemp != iSection->second.end()) {
2155  if (!IsLess(a_pKey, iTemp->first.pItem)) {
2156  *a_pHasMultiple = true;
2157  }
2158  }
2159  }
2160 
2161  return iKeyVal->second;
2162 }
2163 
2164 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2165 long
2167  const SI_CHAR * a_pSection,
2168  const SI_CHAR * a_pKey,
2169  long a_nDefault,
2170  bool * a_pHasMultiple
2171  ) const
2172 {
2173  // return the default if we don't have a value
2174  const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2175  if (!pszValue || !*pszValue) return a_nDefault;
2176 
2177  // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2178  char szValue[64] = { 0 };
2179  SI_CONVERTER c(m_bStoreIsUtf8);
2180  if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2181  return a_nDefault;
2182  }
2183 
2184  // handle the value as hex if prefaced with "0x"
2185  long nValue = a_nDefault;
2186  char * pszSuffix = szValue;
2187  if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
2188  if (!szValue[2]) return a_nDefault;
2189  nValue = strtol(&szValue[2], &pszSuffix, 16);
2190  }
2191  else {
2192  nValue = strtol(szValue, &pszSuffix, 10);
2193  }
2194 
2195  // any invalid strings will return the default value
2196  if (*pszSuffix) {
2197  return a_nDefault;
2198  }
2199 
2200  return nValue;
2201 }
2202 
2203 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2204 SI_Error
2206  const SI_CHAR * a_pSection,
2207  const SI_CHAR * a_pKey,
2208  long a_nValue,
2209  const SI_CHAR * a_pComment,
2210  bool a_bUseHex,
2211  bool a_bForceReplace
2212  )
2213 {
2214  // use SetValue to create sections
2215  if (!a_pSection || !a_pKey) return SI_FAIL;
2216 
2217  // convert to an ASCII string
2218  char szInput[64];
2219 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2220  sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2221 #else // !__STDC_WANT_SECURE_LIB__
2222  snprintf(szInput, sizeof(szInput), a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2223 #endif // __STDC_WANT_SECURE_LIB__
2224 
2225  // convert to output text
2226  SI_CHAR szOutput[64];
2227  SI_CONVERTER c(m_bStoreIsUtf8);
2228  c.ConvertFromStore(szInput, strlen(szInput) + 1,
2229  szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2230 
2231  // actually add it
2232  return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2233 }
2234 
2235 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2236 double
2238  const SI_CHAR * a_pSection,
2239  const SI_CHAR * a_pKey,
2240  double a_nDefault,
2241  bool * a_pHasMultiple
2242  ) const
2243 {
2244  // return the default if we don't have a value
2245  const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2246  if (!pszValue || !*pszValue) return a_nDefault;
2247 
2248  // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2249  char szValue[64] = { 0 };
2250  SI_CONVERTER c(m_bStoreIsUtf8);
2251  if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2252  return a_nDefault;
2253  }
2254 
2255  char * pszSuffix = NULL;
2256  double nValue = strtod(szValue, &pszSuffix);
2257 
2258  // any invalid strings will return the default value
2259  if (!pszSuffix || *pszSuffix) {
2260  return a_nDefault;
2261  }
2262 
2263  return nValue;
2264 }
2265 
2266 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2267 SI_Error
2269  const SI_CHAR * a_pSection,
2270  const SI_CHAR * a_pKey,
2271  double a_nValue,
2272  const SI_CHAR * a_pComment,
2273  bool a_bForceReplace
2274  )
2275 {
2276  // use SetValue to create sections
2277  if (!a_pSection || !a_pKey) return SI_FAIL;
2278 
2279  // convert to an ASCII string
2280  char szInput[64];
2281 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2282  sprintf_s(szInput, "%f", a_nValue);
2283 #else // !__STDC_WANT_SECURE_LIB__
2284  snprintf(szInput, sizeof(szInput), "%f", a_nValue);
2285 #endif // __STDC_WANT_SECURE_LIB__
2286 
2287  // convert to output text
2288  SI_CHAR szOutput[64];
2289  SI_CONVERTER c(m_bStoreIsUtf8);
2290  c.ConvertFromStore(szInput, strlen(szInput) + 1,
2291  szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2292 
2293  // actually add it
2294  return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2295 }
2296 
2297 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2298 bool
2300  const SI_CHAR * a_pSection,
2301  const SI_CHAR * a_pKey,
2302  bool a_bDefault,
2303  bool * a_pHasMultiple
2304  ) const
2305 {
2306  // return the default if we don't have a value
2307  const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2308  if (!pszValue || !*pszValue) return a_bDefault;
2309 
2310  // we only look at the minimum number of characters
2311  switch (pszValue[0]) {
2312  case 't': case 'T': // true
2313  case 'y': case 'Y': // yes
2314  case '1': // 1 (one)
2315  return true;
2316 
2317  case 'f': case 'F': // false
2318  case 'n': case 'N': // no
2319  case '0': // 0 (zero)
2320  return false;
2321 
2322  case 'o': case 'O':
2323  if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on
2324  if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off
2325  break;
2326  }
2327 
2328  // no recognized value, return the default
2329  return a_bDefault;
2330 }
2331 
2332 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2333 SI_Error
2335  const SI_CHAR * a_pSection,
2336  const SI_CHAR * a_pKey,
2337  bool a_bValue,
2338  const SI_CHAR * a_pComment,
2339  bool a_bForceReplace
2340  )
2341 {
2342  // use SetValue to create sections
2343  if (!a_pSection || !a_pKey) return SI_FAIL;
2344 
2345  // convert to an ASCII string
2346  const char * pszInput = a_bValue ? "true" : "false";
2347 
2348  // convert to output text
2349  SI_CHAR szOutput[64];
2350  SI_CONVERTER c(m_bStoreIsUtf8);
2351  c.ConvertFromStore(pszInput, strlen(pszInput) + 1,
2352  szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2353 
2354  // actually add it
2355  return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2356 }
2357 
2358 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2359 bool
2361  const SI_CHAR * a_pSection,
2362  const SI_CHAR * a_pKey,
2363  TNamesDepend & a_values
2364  ) const
2365 {
2366  a_values.clear();
2367 
2368  if (!a_pSection || !a_pKey) {
2369  return false;
2370  }
2371  typename TSection::const_iterator iSection = m_data.find(a_pSection);
2372  if (iSection == m_data.end()) {
2373  return false;
2374  }
2375  typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2376  if (iKeyVal == iSection->second.end()) {
2377  return false;
2378  }
2379 
2380  // insert all values for this key
2381  a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2382  if (m_bAllowMultiKey) {
2383  ++iKeyVal;
2384  while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {
2385  a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2386  ++iKeyVal;
2387  }
2388  }
2389 
2390  return true;
2391 }
2392 
2393 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2394 int
2396  const SI_CHAR * a_pSection
2397  ) const
2398 {
2399  if (!a_pSection) {
2400  return -1;
2401  }
2402 
2403  typename TSection::const_iterator iSection = m_data.find(a_pSection);
2404  if (iSection == m_data.end()) {
2405  return -1;
2406  }
2407  const TKeyVal & section = iSection->second;
2408 
2409  // if multi-key isn't permitted then the section size is
2410  // the number of keys that we have.
2411  if (!m_bAllowMultiKey || section.empty()) {
2412  return (int) section.size();
2413  }
2414 
2415  // otherwise we need to count them
2416  int nCount = 0;
2417  const SI_CHAR * pLastKey = NULL;
2418  typename TKeyVal::const_iterator iKeyVal = section.begin();
2419  for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
2420  if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2421  ++nCount;
2422  pLastKey = iKeyVal->first.pItem;
2423  }
2424  }
2425  return nCount;
2426 }
2427 
2428 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2431  const SI_CHAR * a_pSection
2432  ) const
2433 {
2434  if (a_pSection) {
2435  typename TSection::const_iterator i = m_data.find(a_pSection);
2436  if (i != m_data.end()) {
2437  return &(i->second);
2438  }
2439  }
2440  return 0;
2441 }
2442 
2443 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2444 void
2446  TNamesDepend & a_names
2447  ) const
2448 {
2449  a_names.clear();
2450  typename TSection::const_iterator i = m_data.begin();
2451  for (int n = 0; i != m_data.end(); ++i, ++n ) {
2452  a_names.push_back(i->first);
2453  }
2454 }
2455 
2456 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2457 bool
2459  const SI_CHAR * a_pSection,
2460  TNamesDepend & a_names
2461  ) const
2462 {
2463  a_names.clear();
2464 
2465  if (!a_pSection) {
2466  return false;
2467  }
2468 
2469  typename TSection::const_iterator iSection = m_data.find(a_pSection);
2470  if (iSection == m_data.end()) {
2471  return false;
2472  }
2473 
2474  const TKeyVal & section = iSection->second;
2475  const SI_CHAR * pLastKey = NULL;
2476  typename TKeyVal::const_iterator iKeyVal = section.begin();
2477  for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) {
2478  if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2479  a_names.push_back(iKeyVal->first);
2480  pLastKey = iKeyVal->first.pItem;
2481  }
2482  }
2483 
2484  return true;
2485 }
2486 
2487 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2488 SI_Error
2490  const char * a_pszFile,
2491  bool a_bAddSignature
2492  ) const
2493 {
2494  FILE * fp = NULL;
2495 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2496  fopen_s(&fp, a_pszFile, "wb");
2497 #else // !__STDC_WANT_SECURE_LIB__
2498  fp = fopen(a_pszFile, "wb");
2499 #endif // __STDC_WANT_SECURE_LIB__
2500  if (!fp) return SI_FILE;
2501  SI_Error rc = SaveFile(fp, a_bAddSignature);
2502  fclose(fp);
2503  return rc;
2504 }
2505 
2506 #ifdef SI_HAS_WIDE_FILE
2507 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2508 SI_Error
2510  const SI_WCHAR_T * a_pwszFile,
2511  bool a_bAddSignature
2512  ) const
2513 {
2514 #ifdef _WIN32
2515  FILE * fp = NULL;
2516 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2517  _wfopen_s(&fp, a_pwszFile, L"wb");
2518 #else // !__STDC_WANT_SECURE_LIB__
2519  fp = _wfopen(a_pwszFile, L"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 #else // !_WIN32 (therefore SI_CONVERT_ICU)
2526  char szFile[256];
2527  u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
2528  return SaveFile(szFile, a_bAddSignature);
2529 #endif // _WIN32
2530 }
2531 #endif // SI_HAS_WIDE_FILE
2532 
2533 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2534 SI_Error
2536  FILE * a_pFile,
2537  bool a_bAddSignature
2538  ) const
2539 {
2540  FileWriter writer(a_pFile);
2541  return Save(writer, a_bAddSignature);
2542 }
2543 
2544 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2545 SI_Error
2547  OutputWriter & a_oOutput,
2548  bool a_bAddSignature
2549  ) const
2550 {
2551  Converter convert(m_bStoreIsUtf8);
2552 
2553  // add the UTF-8 signature if it is desired
2554  if (m_bStoreIsUtf8 && a_bAddSignature) {
2555  a_oOutput.Write(SI_UTF8_SIGNATURE);
2556  }
2557 
2558  // get all of the sections sorted in load order
2559  TNamesDepend oSections;
2560  GetAllSections(oSections);
2561 #if defined(_MSC_VER) && _MSC_VER <= 1200
2562  oSections.sort();
2563 #elif defined(__BORLANDC__)
2564  oSections.sort(Entry::LoadOrder());
2565 #else
2566  oSections.sort(typename Entry::LoadOrder());
2567 #endif
2568 
2569  // if there is an empty section name, then it must be written out first
2570  // regardless of the load order
2571  typename TNamesDepend::iterator is = oSections.begin();
2572  for (; is != oSections.end(); ++is) {
2573  if (!*is->pItem) {
2574  // move the empty section name to the front of the section list
2575  if (is != oSections.begin()) {
2576  oSections.splice(oSections.begin(), oSections, is, std::next(is));
2577  }
2578  break;
2579  }
2580  }
2581 
2582  // write the file comment if we have one
2583  bool bNeedNewLine = false;
2584  if (m_pFileComment) {
2585  if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
2586  return SI_FAIL;
2587  }
2588  bNeedNewLine = true;
2589  }
2590 
2591  // iterate through our sections and output the data
2592  typename TNamesDepend::const_iterator iSection = oSections.begin();
2593  for ( ; iSection != oSections.end(); ++iSection ) {
2594  // write out the comment if there is one
2595  if (iSection->pComment) {
2596  if (bNeedNewLine) {
2597  a_oOutput.Write(SI_NEWLINE_A);
2598  a_oOutput.Write(SI_NEWLINE_A);
2599  }
2600  if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) {
2601  return SI_FAIL;
2602  }
2603  bNeedNewLine = false;
2604  }
2605 
2606  if (bNeedNewLine) {
2607  a_oOutput.Write(SI_NEWLINE_A);
2608  a_oOutput.Write(SI_NEWLINE_A);
2609  bNeedNewLine = false;
2610  }
2611 
2612  // write the section (unless there is no section name)
2613  if (*iSection->pItem) {
2614  if (!convert.ConvertToStore(iSection->pItem)) {
2615  return SI_FAIL;
2616  }
2617  a_oOutput.Write("[");
2618  a_oOutput.Write(convert.Data());
2619  a_oOutput.Write("]");
2620  a_oOutput.Write(SI_NEWLINE_A);
2621  }
2622 
2623  // get all of the keys sorted in load order
2624  TNamesDepend oKeys;
2625  GetAllKeys(iSection->pItem, oKeys);
2626 #if defined(_MSC_VER) && _MSC_VER <= 1200
2627  oKeys.sort();
2628 #elif defined(__BORLANDC__)
2629  oKeys.sort(Entry::LoadOrder());
2630 #else
2631  oKeys.sort(typename Entry::LoadOrder());
2632 #endif
2633 
2634  // write all keys and values
2635  typename TNamesDepend::const_iterator iKey = oKeys.begin();
2636  for ( ; iKey != oKeys.end(); ++iKey) {
2637  // get all values for this key
2638  TNamesDepend oValues;
2639  GetAllValues(iSection->pItem, iKey->pItem, oValues);
2640 
2641  typename TNamesDepend::const_iterator iValue = oValues.begin();
2642  for ( ; iValue != oValues.end(); ++iValue) {
2643  // write out the comment if there is one
2644  if (iValue->pComment) {
2645  a_oOutput.Write(SI_NEWLINE_A);
2646  if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) {
2647  return SI_FAIL;
2648  }
2649  }
2650 
2651  // write the key
2652  if (!convert.ConvertToStore(iKey->pItem)) {
2653  return SI_FAIL;
2654  }
2655  a_oOutput.Write(convert.Data());
2656 
2657  // write the value as long
2658  if (*iValue->pItem || !m_bAllowKeyOnly) {
2659  if (!convert.ConvertToStore(iValue->pItem)) {
2660  return SI_FAIL;
2661  }
2662  a_oOutput.Write(m_bSpaces ? " = " : "=");
2663  if (m_bParseQuotes && IsSingleLineQuotedValue(iValue->pItem)) {
2664  // the only way to preserve external whitespace on a value (i.e. before or after)
2665  // is to quote it. This is simple quoting, we don't escape quotes within the data.
2666  a_oOutput.Write("\"");
2667  a_oOutput.Write(convert.Data());
2668  a_oOutput.Write("\"");
2669  }
2670  else if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
2671  // multi-line data needs to be processed specially to ensure
2672  // that we use the correct newline format for the current system
2673  a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
2674  if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
2675  return SI_FAIL;
2676  }
2677  a_oOutput.Write("END_OF_TEXT");
2678  }
2679  else {
2680  a_oOutput.Write(convert.Data());
2681  }
2682  }
2683  a_oOutput.Write(SI_NEWLINE_A);
2684  }
2685  }
2686 
2687  bNeedNewLine = true;
2688  }
2689 
2690  return SI_OK;
2691 }
2692 
2693 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2694 bool
2696  OutputWriter & a_oOutput,
2697  Converter & a_oConverter,
2698  const SI_CHAR * a_pText
2699  ) const
2700 {
2701  const SI_CHAR * pEndOfLine;
2702  SI_CHAR cEndOfLineChar = *a_pText;
2703  while (cEndOfLineChar) {
2704  // find the end of this line
2705  pEndOfLine = a_pText;
2706  for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;
2707  cEndOfLineChar = *pEndOfLine;
2708 
2709  // temporarily null terminate, convert and output the line
2710  *const_cast<SI_CHAR*>(pEndOfLine) = 0;
2711  if (!a_oConverter.ConvertToStore(a_pText)) {
2712  return false;
2713  }
2714  *const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar;
2715  a_pText += (pEndOfLine - a_pText) + 1;
2716  a_oOutput.Write(a_oConverter.Data());
2717  a_oOutput.Write(SI_NEWLINE_A);
2718  }
2719  return true;
2720 }
2721 
2722 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2723 bool
2725  const SI_CHAR * a_pSection,
2726  const SI_CHAR * a_pKey,
2727  bool a_bRemoveEmpty
2728  )
2729 {
2730  return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty);
2731 }
2732 
2733 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2734 bool
2736  const SI_CHAR * a_pSection,
2737  const SI_CHAR * a_pKey,
2738  const SI_CHAR * a_pValue,
2739  bool a_bRemoveEmpty
2740  )
2741 {
2742  if (!a_pSection) {
2743  return false;
2744  }
2745 
2746  typename TSection::iterator iSection = m_data.find(a_pSection);
2747  if (iSection == m_data.end()) {
2748  return false;
2749  }
2750 
2751  // remove a single key if we have a keyname
2752  if (a_pKey) {
2753  typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
2754  if (iKeyVal == iSection->second.end()) {
2755  return false;
2756  }
2757 
2758  const static SI_STRLESS isLess = SI_STRLESS();
2759 
2760  // remove any copied strings and then the key
2761  typename TKeyVal::iterator iDelete;
2762  bool bDeleted = false;
2763  do {
2764  iDelete = iKeyVal++;
2765 
2766  if(a_pValue == NULL ||
2767  (isLess(a_pValue, iDelete->second) == false &&
2768  isLess(iDelete->second, a_pValue) == false)) {
2769  DeleteString(iDelete->first.pItem);
2770  DeleteString(iDelete->second);
2771  iSection->second.erase(iDelete);
2772  bDeleted = true;
2773  }
2774  }
2775  while (iKeyVal != iSection->second.end()
2776  && !IsLess(a_pKey, iKeyVal->first.pItem));
2777 
2778  if(!bDeleted) {
2779  return false;
2780  }
2781 
2782  // done now if the section is not empty or we are not pruning away
2783  // the empty sections. Otherwise let it fall through into the section
2784  // deletion code
2785  if (!a_bRemoveEmpty || !iSection->second.empty()) {
2786  return true;
2787  }
2788  }
2789  else {
2790  // delete all copied strings from this section. The actual
2791  // entries will be removed when the section is removed.
2792  typename TKeyVal::iterator iKeyVal = iSection->second.begin();
2793  for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
2794  DeleteString(iKeyVal->first.pItem);
2795  DeleteString(iKeyVal->second);
2796  }
2797  }
2798 
2799  // delete the section itself
2800  DeleteString(iSection->first.pItem);
2801  m_data.erase(iSection);
2802 
2803  return true;
2804 }
2805 
2806 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2807 void
2809  const SI_CHAR * a_pString
2810  )
2811 {
2812  // strings may exist either inside the data block, or they will be
2813  // individually allocated and stored in m_strings. We only physically
2814  // delete those stored in m_strings.
2815  if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
2816  typename TNamesDepend::iterator i = m_strings.begin();
2817  for (;i != m_strings.end(); ++i) {
2818  if (a_pString == i->pItem) {
2819  delete[] const_cast<SI_CHAR*>(i->pItem);
2820  m_strings.erase(i);
2821  break;
2822  }
2823  }
2824  }
2825 }
2826 
2827 // ---------------------------------------------------------------------------
2828 // CONVERSION FUNCTIONS
2829 // ---------------------------------------------------------------------------
2830 
2831 // Defines the conversion classes for different libraries. Before including
2832 // SimpleIni.h, set the converter that you wish you use by defining one of the
2833 // following symbols.
2834 //
2835 // SI_NO_CONVERSION Do not make the "W" wide character version of the
2836 // library available. Only CSimpleIniA etc is defined.
2837 // Default on Linux/MacOS/etc.
2838 // SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
2839 // Default on Windows.
2840 // SI_CONVERT_GENERIC Use the Unicode reference conversion library in
2841 // the accompanying files ConvertUTF.h/c
2842 // SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
2843 // ICU headers on include path and icuuc.lib
2844 
2845 #if !defined(SI_NO_CONVERSION) && !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
2846 # ifdef _WIN32
2847 # define SI_CONVERT_WIN32
2848 # else
2849 # define SI_NO_CONVERSION
2850 # endif
2851 #endif
2852 
2858 template<class SI_CHAR>
2860  bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2861  long cmp;
2862  for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2863  cmp = (long) *pLeft - (long) *pRight;
2864  if (cmp != 0) {
2865  return cmp < 0;
2866  }
2867  }
2868  return *pRight != 0;
2869  }
2870 };
2871 
2878 template<class SI_CHAR>
2880  inline SI_CHAR locase(SI_CHAR ch) const {
2881  return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
2882  }
2883  bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2884  long cmp;
2885  for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2886  cmp = (long) locase(*pLeft) - (long) locase(*pRight);
2887  if (cmp != 0) {
2888  return cmp < 0;
2889  }
2890  }
2891  return *pRight != 0;
2892  }
2893 };
2894 
2898 template<class SI_CHAR>
2900  bool m_bStoreIsUtf8;
2901 protected:
2902  SI_ConvertA() { }
2903 public:
2904  SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
2905 
2906  /* copy and assignment */
2907  SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }
2908  SI_ConvertA & operator=(const SI_ConvertA & rhs) {
2909  m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2910  return *this;
2911  }
2912 
2927  const char * a_pInputData,
2928  size_t a_uInputDataLen)
2929  {
2930  (void)a_pInputData;
2931  SI_ASSERT(a_uInputDataLen != (size_t) -1);
2932 
2933  // ASCII/MBCS/UTF-8 needs no conversion
2934  return a_uInputDataLen;
2935  }
2936 
2951  const char * a_pInputData,
2952  size_t a_uInputDataLen,
2953  SI_CHAR * a_pOutputData,
2954  size_t a_uOutputDataSize)
2955  {
2956  // ASCII/MBCS/UTF-8 needs no conversion
2957  if (a_uInputDataLen > a_uOutputDataSize) {
2958  return false;
2959  }
2960  memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
2961  return true;
2962  }
2963 
2974  size_t SizeToStore(
2975  const SI_CHAR * a_pInputData)
2976  {
2977  // ASCII/MBCS/UTF-8 needs no conversion
2978  return strlen((const char *)a_pInputData) + 1;
2979  }
2980 
2995  const SI_CHAR * a_pInputData,
2996  char * a_pOutputData,
2997  size_t a_uOutputDataSize)
2998  {
2999  // calc input string length (SI_CHAR type and size independent)
3000  size_t uInputLen = strlen((const char *)a_pInputData) + 1;
3001  if (uInputLen > a_uOutputDataSize) {
3002  return false;
3003  }
3004 
3005  // ascii/UTF-8 needs no conversion
3006  memcpy(a_pOutputData, a_pInputData, uInputLen);
3007  return true;
3008  }
3009 };
3010 
3011 
3012 // ---------------------------------------------------------------------------
3013 // SI_CONVERT_GENERIC
3014 // ---------------------------------------------------------------------------
3015 #ifdef SI_CONVERT_GENERIC
3016 
3017 #define SI_Case SI_GenericCase
3018 #define SI_NoCase SI_GenericNoCase
3019 
3020 #include <wchar.h>
3021 #include "ConvertUTF.h"
3022 
3027 template<class SI_CHAR>
3028 class SI_ConvertW {
3029  bool m_bStoreIsUtf8;
3030 protected:
3031  SI_ConvertW() { }
3032 public:
3033  SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
3034 
3035  /* copy and assignment */
3036  SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
3037  SI_ConvertW & operator=(const SI_ConvertW & rhs) {
3038  m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
3039  return *this;
3040  }
3041 
3055  size_t SizeFromStore(
3056  const char * a_pInputData,
3057  size_t a_uInputDataLen)
3058  {
3059  SI_ASSERT(a_uInputDataLen != (size_t) -1);
3060 
3061  if (m_bStoreIsUtf8) {
3062  // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
3063  // so we just return the same number of characters required as for
3064  // the source text.
3065  return a_uInputDataLen;
3066  }
3067 
3068  // get the required buffer size
3069 #if defined(_MSC_VER)
3070  size_t uBufSiz;
3071  errno_t e = mbstowcs_s(&uBufSiz, NULL, 0, a_pInputData, a_uInputDataLen);
3072  return (e == 0) ? uBufSiz : (size_t) -1;
3073 #elif !defined(SI_NO_MBSTOWCS_NULL)
3074  return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
3075 #else
3076  // fall back processing for platforms that don't support a NULL dest to mbstowcs
3077  // worst case scenario is 1:1, this will be a sufficient buffer size
3078  (void)a_pInputData;
3079  return a_uInputDataLen;
3080 #endif
3081  }
3082 
3096  bool ConvertFromStore(
3097  const char * a_pInputData,
3098  size_t a_uInputDataLen,
3099  SI_CHAR * a_pOutputData,
3100  size_t a_uOutputDataSize)
3101  {
3102  if (m_bStoreIsUtf8) {
3103  // This uses the Unicode reference implementation to do the
3104  // conversion from UTF-8 to wchar_t. The required files are
3105  // ConvertUTF.h and ConvertUTF.c which should be included in
3106  // the distribution but are publicly available from unicode.org
3107  // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
3108  ConversionResult retval;
3109  const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
3110  if (sizeof(wchar_t) == sizeof(UTF32)) {
3111  UTF32 * pUtf32 = (UTF32 *) a_pOutputData;
3112  retval = ConvertUTF8toUTF32(
3113  &pUtf8, pUtf8 + a_uInputDataLen,
3114  &pUtf32, pUtf32 + a_uOutputDataSize,
3115  lenientConversion);
3116  }
3117  else if (sizeof(wchar_t) == sizeof(UTF16)) {
3118  UTF16 * pUtf16 = (UTF16 *) a_pOutputData;
3119  retval = ConvertUTF8toUTF16(
3120  &pUtf8, pUtf8 + a_uInputDataLen,
3121  &pUtf16, pUtf16 + a_uOutputDataSize,
3122  lenientConversion);
3123  }
3124  return retval == conversionOK;
3125  }
3126 
3127  // convert to wchar_t
3128 #if defined(_MSC_VER)
3129  size_t uBufSiz;
3130  errno_t e = mbstowcs_s(&uBufSiz,
3131  a_pOutputData, a_uOutputDataSize,
3132  a_pInputData, a_uInputDataLen);
3133  (void)uBufSiz;
3134  return (e == 0);
3135 #else
3136  size_t retval = mbstowcs(a_pOutputData,
3137  a_pInputData, a_uOutputDataSize);
3138  return retval != (size_t)(-1);
3139 #endif
3140  }
3141 
3152  size_t SizeToStore(
3153  const SI_CHAR * a_pInputData)
3154  {
3155  if (m_bStoreIsUtf8) {
3156  // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
3157  size_t uLen = 0;
3158  while (a_pInputData[uLen]) {
3159  ++uLen;
3160  }
3161  return (6 * uLen) + 1;
3162  }
3163  else {
3164  size_t uLen = wcstombs(NULL, a_pInputData, 0);
3165  if (uLen == (size_t)(-1)) {
3166  return uLen;
3167  }
3168  return uLen + 1; // include NULL terminator
3169  }
3170  }
3171 
3185  bool ConvertToStore(
3186  const SI_CHAR * a_pInputData,
3187  char * a_pOutputData,
3188  size_t a_uOutputDataSize
3189  )
3190  {
3191  if (m_bStoreIsUtf8) {
3192  // calc input string length (SI_CHAR type and size independent)
3193  size_t uInputLen = 0;
3194  while (a_pInputData[uInputLen]) {
3195  ++uInputLen;
3196  }
3197  ++uInputLen; // include the NULL char
3198 
3199  // This uses the Unicode reference implementation to do the
3200  // conversion from wchar_t to UTF-8. The required files are
3201  // ConvertUTF.h and ConvertUTF.c which should be included in
3202  // the distribution but are publicly available from unicode.org
3203  // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
3204  ConversionResult retval;
3205  UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
3206  if (sizeof(wchar_t) == sizeof(UTF32)) {
3207  const UTF32 * pUtf32 = (const UTF32 *) a_pInputData;
3208  retval = ConvertUTF32toUTF8(
3209  &pUtf32, pUtf32 + uInputLen,
3210  &pUtf8, pUtf8 + a_uOutputDataSize,
3211  lenientConversion);
3212  }
3213  else if (sizeof(wchar_t) == sizeof(UTF16)) {
3214  const UTF16 * pUtf16 = (const UTF16 *) a_pInputData;
3215  retval = ConvertUTF16toUTF8(
3216  &pUtf16, pUtf16 + uInputLen,
3217  &pUtf8, pUtf8 + a_uOutputDataSize,
3218  lenientConversion);
3219  }
3220  return retval == conversionOK;
3221  }
3222  else {
3223  size_t retval = wcstombs(a_pOutputData,
3224  a_pInputData, a_uOutputDataSize);
3225  return retval != (size_t) -1;
3226  }
3227  }
3228 };
3229 
3230 #endif // SI_CONVERT_GENERIC
3231 
3232 
3233 // ---------------------------------------------------------------------------
3234 // SI_CONVERT_ICU
3235 // ---------------------------------------------------------------------------
3236 #ifdef SI_CONVERT_ICU
3237 
3238 #define SI_Case SI_GenericCase
3239 #define SI_NoCase SI_GenericNoCase
3240 
3241 #include <unicode/ucnv.h>
3242 
3246 template<class SI_CHAR>
3247 class SI_ConvertW {
3248  const char * m_pEncoding;
3249  UConverter * m_pConverter;
3250 protected:
3251  SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
3252 public:
3253  SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
3254  m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
3255  }
3256 
3257  /* copy and assignment */
3258  SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
3259  SI_ConvertW & operator=(const SI_ConvertW & rhs) {
3260  m_pEncoding = rhs.m_pEncoding;
3261  m_pConverter = NULL;
3262  return *this;
3263  }
3264  ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); }
3265 
3279  size_t SizeFromStore(
3280  const char * a_pInputData,
3281  size_t a_uInputDataLen)
3282  {
3283  SI_ASSERT(a_uInputDataLen != (size_t) -1);
3284 
3285  UErrorCode nError;
3286 
3287  if (!m_pConverter) {
3288  nError = U_ZERO_ERROR;
3289  m_pConverter = ucnv_open(m_pEncoding, &nError);
3290  if (U_FAILURE(nError)) {
3291  return (size_t) -1;
3292  }
3293  }
3294 
3295  nError = U_ZERO_ERROR;
3296  int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,
3297  a_pInputData, (int32_t) a_uInputDataLen, &nError);
3298  if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3299  return (size_t) -1;
3300  }
3301 
3302  return (size_t) nLen;
3303  }
3304 
3318  bool ConvertFromStore(
3319  const char * a_pInputData,
3320  size_t a_uInputDataLen,
3321  UChar * a_pOutputData,
3322  size_t a_uOutputDataSize)
3323  {
3324  UErrorCode nError;
3325 
3326  if (!m_pConverter) {
3327  nError = U_ZERO_ERROR;
3328  m_pConverter = ucnv_open(m_pEncoding, &nError);
3329  if (U_FAILURE(nError)) {
3330  return false;
3331  }
3332  }
3333 
3334  nError = U_ZERO_ERROR;
3335  ucnv_toUChars(m_pConverter,
3336  a_pOutputData, (int32_t) a_uOutputDataSize,
3337  a_pInputData, (int32_t) a_uInputDataLen, &nError);
3338  if (U_FAILURE(nError)) {
3339  return false;
3340  }
3341 
3342  return true;
3343  }
3344 
3355  size_t SizeToStore(
3356  const UChar * a_pInputData)
3357  {
3358  UErrorCode nError;
3359 
3360  if (!m_pConverter) {
3361  nError = U_ZERO_ERROR;
3362  m_pConverter = ucnv_open(m_pEncoding, &nError);
3363  if (U_FAILURE(nError)) {
3364  return (size_t) -1;
3365  }
3366  }
3367 
3368  nError = U_ZERO_ERROR;
3369  int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,
3370  a_pInputData, -1, &nError);
3371  if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3372  return (size_t) -1;
3373  }
3374 
3375  return (size_t) nLen + 1;
3376  }
3377 
3391  bool ConvertToStore(
3392  const UChar * a_pInputData,
3393  char * a_pOutputData,
3394  size_t a_uOutputDataSize)
3395  {
3396  UErrorCode nError;
3397 
3398  if (!m_pConverter) {
3399  nError = U_ZERO_ERROR;
3400  m_pConverter = ucnv_open(m_pEncoding, &nError);
3401  if (U_FAILURE(nError)) {
3402  return false;
3403  }
3404  }
3405 
3406  nError = U_ZERO_ERROR;
3407  ucnv_fromUChars(m_pConverter,
3408  a_pOutputData, (int32_t) a_uOutputDataSize,
3409  a_pInputData, -1, &nError);
3410  if (U_FAILURE(nError)) {
3411  return false;
3412  }
3413 
3414  return true;
3415  }
3416 };
3417 
3418 #endif // SI_CONVERT_ICU
3419 
3420 
3421 // ---------------------------------------------------------------------------
3422 // SI_CONVERT_WIN32
3423 // ---------------------------------------------------------------------------
3424 #ifdef SI_CONVERT_WIN32
3425 
3426 #define SI_Case SI_GenericCase
3427 
3428 // Windows CE doesn't have errno or MBCS libraries
3429 #ifdef _WIN32_WCE
3430 # ifndef SI_NO_MBCS
3431 # define SI_NO_MBCS
3432 # endif
3433 #endif
3434 
3435 #include <windows.h>
3436 #ifdef SI_NO_MBCS
3437 # define SI_NoCase SI_GenericNoCase
3438 #else // !SI_NO_MBCS
3447 #include <mbstring.h>
3448 template<class SI_CHAR>
3449 struct SI_NoCase {
3450  bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
3451  if (sizeof(SI_CHAR) == sizeof(char)) {
3452  return _mbsicmp((const unsigned char *)pLeft,
3453  (const unsigned char *)pRight) < 0;
3454  }
3455  if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
3456  return _wcsicmp((const wchar_t *)pLeft,
3457  (const wchar_t *)pRight) < 0;
3458  }
3459  return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight);
3460  }
3461 };
3462 #endif // SI_NO_MBCS
3463 
3470 template<class SI_CHAR>
3471 class SI_ConvertW {
3472  UINT m_uCodePage;
3473 protected:
3474  SI_ConvertW() { }
3475 public:
3476  SI_ConvertW(bool a_bStoreIsUtf8) {
3477  m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP;
3478  }
3479 
3480  /* copy and assignment */
3481  SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
3482  SI_ConvertW & operator=(const SI_ConvertW & rhs) {
3483  m_uCodePage = rhs.m_uCodePage;
3484  return *this;
3485  }
3486 
3500  size_t SizeFromStore(
3501  const char * a_pInputData,
3502  size_t a_uInputDataLen)
3503  {
3504  SI_ASSERT(a_uInputDataLen != (size_t) -1);
3505 
3506  int retval = MultiByteToWideChar(
3507  m_uCodePage, 0,
3508  a_pInputData, (int) a_uInputDataLen,
3509  0, 0);
3510  return (size_t)(retval > 0 ? retval : -1);
3511  }
3512 
3526  bool ConvertFromStore(
3527  const char * a_pInputData,
3528  size_t a_uInputDataLen,
3529  SI_CHAR * a_pOutputData,
3530  size_t a_uOutputDataSize)
3531  {
3532  int nSize = MultiByteToWideChar(
3533  m_uCodePage, 0,
3534  a_pInputData, (int) a_uInputDataLen,
3535  (wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
3536  return (nSize > 0);
3537  }
3538 
3549  size_t SizeToStore(
3550  const SI_CHAR * a_pInputData)
3551  {
3552  int retval = WideCharToMultiByte(
3553  m_uCodePage, 0,
3554  (const wchar_t *) a_pInputData, -1,
3555  0, 0, 0, 0);
3556  return (size_t) (retval > 0 ? retval : -1);
3557  }
3558 
3572  bool ConvertToStore(
3573  const SI_CHAR * a_pInputData,
3574  char * a_pOutputData,
3575  size_t a_uOutputDataSize)
3576  {
3577  int retval = WideCharToMultiByte(
3578  m_uCodePage, 0,
3579  (const wchar_t *) a_pInputData, -1,
3580  a_pOutputData, (int) a_uOutputDataSize, 0, 0);
3581  return retval > 0;
3582  }
3583 };
3584 
3585 #endif // SI_CONVERT_WIN32
3586 
3587 
3588 
3589 // ---------------------------------------------------------------------------
3590 // SI_NO_CONVERSION
3591 // ---------------------------------------------------------------------------
3592 #ifdef SI_NO_CONVERSION
3593 
3594 #define SI_Case SI_GenericCase
3595 #define SI_NoCase SI_GenericNoCase
3596 
3597 #endif // SI_NO_CONVERSION
3598 
3599 
3600 
3601 // ---------------------------------------------------------------------------
3602 // TYPE DEFINITIONS
3603 // ---------------------------------------------------------------------------
3604 
3605 typedef CSimpleIniTempl<char,
3606  SI_NoCase<char>,SI_ConvertA<char> > CSimpleIniA;
3607 typedef CSimpleIniTempl<char,
3608  SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA;
3609 
3610 #if defined(SI_NO_CONVERSION)
3611 // if there is no wide char conversion then we don't need to define the
3612 // widechar "W" versions of CSimpleIni
3613 # define CSimpleIni CSimpleIniA
3614 # define CSimpleIniCase CSimpleIniCaseA
3615 # define SI_NEWLINE SI_NEWLINE_A
3616 #else
3617 # if defined(SI_CONVERT_ICU)
3618 typedef CSimpleIniTempl<UChar,
3619  SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;
3620 typedef CSimpleIniTempl<UChar,
3621  SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;
3622 # else
3623 typedef CSimpleIniTempl<wchar_t,
3624  SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;
3625 typedef CSimpleIniTempl<wchar_t,
3626  SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;
3627 # endif
3628 
3629 # ifdef _UNICODE
3630 # define CSimpleIni CSimpleIniW
3631 # define CSimpleIniCase CSimpleIniCaseW
3632 # define SI_NEWLINE SI_NEWLINE_W
3633 # else // !_UNICODE
3634 # define CSimpleIni CSimpleIniA
3635 # define CSimpleIniCase CSimpleIniCaseA
3636 # define SI_NEWLINE SI_NEWLINE_A
3637 # endif // _UNICODE
3638 #endif
3639 
3640 #ifdef _MSC_VER
3641 # pragma warning (pop)
3642 #endif
3643 
3644 #endif // INCLUDED_SimpleIni_h
3645 
Definition: SimpleIni.h:433
Definition: SimpleIni.h:390
Definition: SimpleIni.h:379
Definition: SimpleIni.h:403
Definition: SimpleIni.h:312
bool DeleteValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pValue, bool a_bRemoveEmpty=false)
Definition: SimpleIni.h:2735
bool GetAllKeys(const SI_CHAR *a_pSection, TNamesDepend &a_names) const
Definition: SimpleIni.h:2458
int GetSectionSize(const SI_CHAR *a_pSection) const
Definition: SimpleIni.h:2395
void SetAllowKeyOnly(bool a_bAllowKeyOnly=true)
Definition: SimpleIni.h:581
SI_Error LoadData(const std::string &a_strData)
Definition: SimpleIni.h:647
bool UsingQuotes() const
Definition: SimpleIni.h:571
std::map< Entry, TKeyVal, typename Entry::KeyOrder > TSection
Definition: SimpleIni.h:369
std::list< Entry > TNamesDepend
Definition: SimpleIni.h:374
bool IsUnicode() const
Definition: SimpleIni.h:507
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:2334
bool IsMultiKey() const
Definition: SimpleIni.h:532
Converter GetConverter() const
Definition: SimpleIni.h:1184
bool IsEmpty() const
Definition: SimpleIni.h:483
const TKeyVal * GetSection(const SI_CHAR *a_pSection) const
Definition: SimpleIni.h:2430
SI_Error Save(OutputWriter &a_oOutput, bool a_bAddSignature=false) const
Definition: SimpleIni.h:2546
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:2129
bool GetBoolValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bDefault=false, bool *a_pHasMultiple=NULL) const
Definition: SimpleIni.h:2299
long GetLongValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, long a_nDefault=0, bool *a_pHasMultiple=NULL) const
Definition: SimpleIni.h:2166
void GetAllSections(TNamesDepend &a_names) const
Definition: SimpleIni.h:2445
void Reset()
Definition: SimpleIni.h:1375
~CSimpleIniTempl()
Definition: SimpleIni.h:1368
bool UsingSpaces() const
Definition: SimpleIni.h:559
SI_Error Save(std::string &a_sBuffer, bool a_bAddSignature=false) const
Definition: SimpleIni.h:787
bool KeyExists(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey) const
Definition: SimpleIni.h:900
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:1025
bool SectionExists(const SI_CHAR *a_pSection) const
Definition: SimpleIni.h:893
bool Delete(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bRemoveEmpty=false)
Definition: SimpleIni.h:2724
void SetMultiLine(bool a_bAllowMultiLine=true)
Definition: SimpleIni.h:541
void SetQuotes(bool a_bParseQuotes=true)
Definition: SimpleIni.h:566
void SetUnicode(bool a_bIsUtf8=true)
Definition: SimpleIni.h:502
double GetDoubleValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, double a_nDefault=0, bool *a_pHasMultiple=NULL) const
Definition: SimpleIni.h:2237
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:2205
void SetMultiKey(bool a_bAllowMultiKey=true)
Definition: SimpleIni.h:527
bool GetAllValues(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, TNamesDepend &a_values) const
Definition: SimpleIni.h:2360
void SetSpaces(bool a_bSpaces=true)
Definition: SimpleIni.h:554
std::multimap< Entry, const SI_CHAR *, typename Entry::KeyOrder > TKeyVal
Definition: SimpleIni.h:366
SI_Error LoadFile(const char *a_pszFile)
Definition: SimpleIni.h:1398
bool GetAllowKeyOnly() const
Definition: SimpleIni.h:586
SI_Error SaveFile(const char *a_pszFile, bool a_bAddSignature=true) const
Definition: SimpleIni.h:2489
CSimpleIniTempl(bool a_bIsUtf8=false, bool a_bMultiKey=false, bool a_bMultiLine=false)
Definition: SimpleIni.h:1349
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:2268
bool IsMultiLine() const
Definition: SimpleIni.h:546
Definition: SimpleIni.h:2899
bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData, size_t a_uOutputDataSize)
Definition: SimpleIni.h:2994
size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen)
Definition: SimpleIni.h:2926
size_t SizeToStore(const SI_CHAR *a_pInputData)
Definition: SimpleIni.h:2974
bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen, SI_CHAR *a_pOutputData, size_t a_uOutputDataSize)
Definition: SimpleIni.h:2950
Definition: SimpleIni.h:347
Definition: SimpleIni.h:355
Definition: SimpleIni.h:317
Definition: SimpleIni.h:2859
Definition: SimpleIni.h:2879