YDLIDAR-SS SDK  V2.0.0
SimpleIni.h
1 
195 #ifndef INCLUDED_SimpleIni_h
196 #define INCLUDED_SimpleIni_h
197 
198 #if defined(_MSC_VER) && (_MSC_VER >= 1020)
199 # pragma once
200 #endif
201 
202 // Disable these warnings in MSVC:
203 // 4127 "conditional expression is constant" as the conversion classes trigger
204 // it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
205 // be optimized away in a release build.
206 // 4503 'insert' : decorated name length exceeded, name was truncated
207 // 4702 "unreachable code" as the MS STL header causes it in release mode.
208 // Again, the code causing the warning will be cleaned up by the compiler.
209 // 4786 "identifier truncated to 256 characters" as this is thrown hundreds
210 // of times VC6 as soon as STL is used.
211 #ifdef _MSC_VER
212 # pragma warning (push)
213 # pragma warning (disable: 4127 4503 4702 4786)
214 #endif
215 
216 #include <cstring>
217 #include <cstdlib>
218 #include <string>
219 #include <map>
220 #include <list>
221 #include <algorithm>
222 #include <stdio.h>
223 
224 #ifdef SI_SUPPORT_IOSTREAMS
225 # include <iostream>
226 #endif // SI_SUPPORT_IOSTREAMS
227 
228 #ifdef _DEBUG
229 # ifndef assert
230 # include <cassert>
231 # endif
232 # define SI_ASSERT(x) assert(x)
233 #else
234 # define SI_ASSERT(x)
235 #endif
236 
237 enum SI_Error {
238  SI_OK = 0,
239  SI_UPDATED = 1,
240  SI_INSERTED = 2,
241 
242  // note: test for any error with (retval < 0)
243  SI_FAIL = -1,
244  SI_NOMEM = -2,
245  SI_FILE = -3
246 };
247 
248 #define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
249 
250 #ifdef _WIN32
251 # define SI_NEWLINE_A "\r\n"
252 # define SI_NEWLINE_W L"\r\n"
253 #else // !_WIN32
254 # define SI_NEWLINE_A "\n"
255 # define SI_NEWLINE_W L"\n"
256 #endif // _WIN32
257 
258 #if defined(SI_CONVERT_ICU)
259 # include <unicode/ustring.h>
260 #endif
261 
262 #if defined(_WIN32)
263 # define SI_HAS_WIDE_FILE
264 # define SI_WCHAR_T wchar_t
265 #elif defined(SI_CONVERT_ICU)
266 # define SI_HAS_WIDE_FILE
267 # define SI_WCHAR_T UChar
268 #endif
269 
270 
271 // ---------------------------------------------------------------------------
272 // MAIN TEMPLATE CLASS
273 // ---------------------------------------------------------------------------
274 
294 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
296  public:
297  typedef SI_CHAR SI_CHAR_T;
298 
300  struct Entry {
301  const SI_CHAR *pItem;
302  const SI_CHAR *pComment;
303  int nOrder;
304 
305  Entry(const SI_CHAR *a_pszItem = NULL, int a_nOrder = 0)
306  : pItem(a_pszItem)
307  , pComment(NULL)
308  , nOrder(a_nOrder)
309  { }
310  Entry(const SI_CHAR *a_pszItem, const SI_CHAR *a_pszComment, int a_nOrder)
311  : pItem(a_pszItem)
312  , pComment(a_pszComment)
313  , nOrder(a_nOrder)
314  { }
315  Entry(const Entry &rhs) {
316  operator=(rhs);
317  }
318  Entry &operator=(const Entry &rhs) {
319  pItem = rhs.pItem;
320  pComment = rhs.pComment;
321  nOrder = rhs.nOrder;
322  return *this;
323  }
324 
325 #if defined(_MSC_VER) && _MSC_VER <= 1200
326 
327  bool operator<(const Entry &rhs) const {
328  return LoadOrder()(*this, rhs);
329  }
330  bool operator>(const Entry &rhs) const {
331  return LoadOrder()(rhs, *this);
332  }
333 #endif
334 
336  struct KeyOrder {
337  bool operator()(const Entry &lhs, const Entry &rhs) const {
338  const static SI_STRLESS isLess = SI_STRLESS();
339  return isLess(lhs.pItem, rhs.pItem);
340  }
341  };
342 
344  struct LoadOrder {
345  bool operator()(const Entry &lhs, const Entry &rhs) const {
346  if (lhs.nOrder != rhs.nOrder) {
347  return lhs.nOrder < rhs.nOrder;
348  }
349 
350  return KeyOrder()(lhs.pItem, rhs.pItem);
351  }
352  };
353  };
354 
356  typedef std::multimap<Entry, const SI_CHAR *, typename Entry::KeyOrder> TKeyVal;
357 
359  typedef std::map<Entry, TKeyVal, typename Entry::KeyOrder> TSection;
360 
364  typedef std::list<Entry> TNamesDepend;
365 
369  class OutputWriter {
370  public:
371  OutputWriter() { }
372  virtual ~OutputWriter() { }
373  virtual void Write(const char *a_pBuf) = 0;
374  private:
375  OutputWriter(const OutputWriter &); // disable
376  OutputWriter &operator=(const OutputWriter &); // disable
377  };
378 
380  class FileWriter : public OutputWriter {
381  FILE *m_file;
382  public:
383  FileWriter(FILE *a_file) : m_file(a_file) { }
384  void Write(const char *a_pBuf) {
385  fputs(a_pBuf, m_file);
386  }
387  private:
388  FileWriter(const FileWriter &); // disable
389  FileWriter &operator=(const FileWriter &); // disable
390  };
391 
393  class StringWriter : public OutputWriter {
394  std::string &m_string;
395  public:
396  StringWriter(std::string &a_string) : m_string(a_string) { }
397  void Write(const char *a_pBuf) {
398  m_string.append(a_pBuf);
399  }
400  private:
401  StringWriter(const StringWriter &); // disable
402  StringWriter &operator=(const StringWriter &); // disable
403  };
404 
405 #ifdef SI_SUPPORT_IOSTREAMS
406 
407  class StreamWriter : public OutputWriter {
408  std::ostream &m_ostream;
409  public:
410  StreamWriter(std::ostream &a_ostream) : m_ostream(a_ostream) { }
411  void Write(const char *a_pBuf) {
412  m_ostream << a_pBuf;
413  }
414  private:
415  StreamWriter(const StreamWriter &); // disable
416  StreamWriter &operator=(const StreamWriter &); // disable
417  };
418 #endif // SI_SUPPORT_IOSTREAMS
419 
423  class Converter : private SI_CONVERTER {
424  public:
425  Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) {
426  m_scratch.resize(1024);
427  }
428  Converter(const Converter &rhs) {
429  operator=(rhs);
430  }
431  Converter &operator=(const Converter &rhs) {
432  m_scratch = rhs.m_scratch;
433  return *this;
434  }
435  bool ConvertToStore(const SI_CHAR *a_pszString) {
436  size_t uLen = SI_CONVERTER::SizeToStore(a_pszString);
437 
438  if (uLen == (size_t)(-1)) {
439  return false;
440  }
441 
442  while (uLen > m_scratch.size()) {
443  m_scratch.resize(m_scratch.size() * 2);
444  }
445 
446  return SI_CONVERTER::ConvertToStore(
447  a_pszString,
448  const_cast<char *>(m_scratch.data()),
449  m_scratch.size());
450  }
451  const char *Data() {
452  return m_scratch.data();
453  }
454  private:
455  std::string m_scratch;
456  };
457 
458  public:
459  /*-----------------------------------------------------------------------*/
460 
468  bool a_bIsUtf8 = false,
469  bool a_bMultiKey = false,
470  bool a_bMultiLine = false
471  );
472 
475 
477  void Reset();
478 
480  bool IsEmpty() const {
481  return m_data.empty();
482  }
483 
484  /*-----------------------------------------------------------------------*/
501  void SetUnicode(bool a_bIsUtf8 = true) {
502  if (!m_pData) {
503  m_bStoreIsUtf8 = a_bIsUtf8;
504  }
505  }
506 
508  bool IsUnicode() const {
509  return m_bStoreIsUtf8;
510  }
511 
530  void SetMultiKey(bool a_bAllowMultiKey = true) {
531  m_bAllowMultiKey = a_bAllowMultiKey;
532  }
533 
535  bool IsMultiKey() const {
536  return m_bAllowMultiKey;
537  }
538 
546  void SetMultiLine(bool a_bAllowMultiLine = true) {
547  m_bAllowMultiLine = a_bAllowMultiLine;
548  }
549 
551  bool IsMultiLine() const {
552  return m_bAllowMultiLine;
553  }
554 
561  void SetSpaces(bool a_bSpaces = true) {
562  m_bSpaces = a_bSpaces;
563  }
564 
566  bool UsingSpaces() const {
567  return m_bSpaces;
568  }
569 
570  /*-----------------------------------------------------------------------*/
582  SI_Error LoadFile(
583  const char *a_pszFile
584  );
585 
586 #ifdef SI_HAS_WIDE_FILE
587 
593  SI_Error LoadFile(
594  const SI_WCHAR_T *a_pwszFile
595  );
596 #endif // SI_HAS_WIDE_FILE
597 
605  SI_Error LoadFile(
606  FILE *a_fpFile
607  );
608 
609 #ifdef SI_SUPPORT_IOSTREAMS
610 
616  SI_Error LoadData(
617  std::istream &a_istream
618  );
619 #endif // SI_SUPPORT_IOSTREAMS
620 
627  SI_Error LoadData(const std::string &a_strData) {
628  return LoadData(a_strData.c_str(), a_strData.size());
629  }
630 
638  SI_Error LoadData(
639  const char *a_pData,
640  size_t a_uDataLen
641  );
642 
643  /*-----------------------------------------------------------------------*/
659  SI_Error SaveFile(
660  const char *a_pszFile,
661  bool a_bAddSignature = true
662  ) const;
663 
664 #ifdef SI_HAS_WIDE_FILE
665 
675  SI_Error SaveFile(
676  const SI_WCHAR_T *a_pwszFile,
677  bool a_bAddSignature = true
678  ) const;
679 #endif // _WIN32
680 
693  SI_Error SaveFile(
694  FILE *a_pFile,
695  bool a_bAddSignature = false
696  ) const;
697 
729  SI_Error Save(
730  OutputWriter &a_oOutput,
731  bool a_bAddSignature = false
732  ) const;
733 
734 #ifdef SI_SUPPORT_IOSTREAMS
735 
746  SI_Error Save(
747  std::ostream &a_ostream,
748  bool a_bAddSignature = false
749  ) const {
750  StreamWriter writer(a_ostream);
751  return Save(writer, a_bAddSignature);
752  }
753 #endif // SI_SUPPORT_IOSTREAMS
754 
766  SI_Error Save(
767  std::string &a_sBuffer,
768  bool a_bAddSignature = false
769  ) const {
770  StringWriter writer(a_sBuffer);
771  return Save(writer, a_bAddSignature);
772  }
773 
774  /*-----------------------------------------------------------------------*/
792  void GetAllSections(
793  TNamesDepend &a_names
794  ) const;
795 
813  bool GetAllKeys(
814  const SI_CHAR *a_pSection,
815  TNamesDepend &a_names
816  ) const;
817 
834  bool GetAllValues(
835  const SI_CHAR *a_pSection,
836  const SI_CHAR *a_pKey,
837  TNamesDepend &a_values
838  ) const;
839 
849  int GetSectionSize(
850  const SI_CHAR *a_pSection
851  ) const;
852 
867  const TKeyVal *GetSection(
868  const SI_CHAR *a_pSection
869  ) const;
870 
888  const SI_CHAR *GetValue(
889  const SI_CHAR *a_pSection,
890  const SI_CHAR *a_pKey,
891  const SI_CHAR *a_pDefault = NULL,
892  bool *a_pHasMultiple = NULL
893  ) const;
894 
908  long GetLongValue(
909  const SI_CHAR *a_pSection,
910  const SI_CHAR *a_pKey,
911  long a_nDefault = 0,
912  bool *a_pHasMultiple = NULL
913  ) const;
914 
928  double GetDoubleValue(
929  const SI_CHAR *a_pSection,
930  const SI_CHAR *a_pKey,
931  double a_nDefault = 0,
932  bool *a_pHasMultiple = NULL
933  ) const;
934 
953  bool GetBoolValue(
954  const SI_CHAR *a_pSection,
955  const SI_CHAR *a_pKey,
956  bool a_bDefault = false,
957  bool *a_pHasMultiple = NULL
958  ) const;
959 
989  SI_Error SetValue(
990  const SI_CHAR *a_pSection,
991  const SI_CHAR *a_pKey,
992  const SI_CHAR *a_pValue,
993  const SI_CHAR *a_pComment = NULL,
994  bool a_bForceReplace = false
995  ) {
996  return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true);
997  }
998 
1022  SI_Error SetLongValue(
1023  const SI_CHAR *a_pSection,
1024  const SI_CHAR *a_pKey,
1025  long a_nValue,
1026  const SI_CHAR *a_pComment = NULL,
1027  bool a_bUseHex = false,
1028  bool a_bForceReplace = false
1029  );
1030 
1051  SI_Error SetDoubleValue(
1052  const SI_CHAR *a_pSection,
1053  const SI_CHAR *a_pKey,
1054  double a_nValue,
1055  const SI_CHAR *a_pComment = NULL,
1056  bool a_bForceReplace = false
1057  );
1058 
1079  SI_Error SetBoolValue(
1080  const SI_CHAR *a_pSection,
1081  const SI_CHAR *a_pKey,
1082  bool a_bValue,
1083  const SI_CHAR *a_pComment = NULL,
1084  bool a_bForceReplace = false
1085  );
1086 
1105  bool Delete(
1106  const SI_CHAR *a_pSection,
1107  const SI_CHAR *a_pKey,
1108  bool a_bRemoveEmpty = false
1109  );
1110 
1131  bool DeleteValue(
1132  const SI_CHAR *a_pSection,
1133  const SI_CHAR *a_pKey,
1134  const SI_CHAR *a_pValue,
1135  bool a_bRemoveEmpty = false
1136  );
1137 
1138  /*-----------------------------------------------------------------------*/
1147  Converter GetConverter() const {
1148  return Converter(m_bStoreIsUtf8);
1149  }
1150 
1151  /*-----------------------------------------------------------------------*/
1154  private:
1155  // copying is not permitted
1156  CSimpleIniTempl(const CSimpleIniTempl &); // disabled
1157  CSimpleIniTempl &operator=(const CSimpleIniTempl &); // disabled
1158 
1161  SI_Error FindFileComment(
1162  SI_CHAR *&a_pData,
1163  bool a_bCopyStrings
1164  );
1165 
1170  bool FindEntry(
1171  SI_CHAR *&a_pData,
1172  const SI_CHAR *&a_pSection,
1173  const SI_CHAR *&a_pKey,
1174  const SI_CHAR *&a_pVal,
1175  const SI_CHAR *&a_pComment
1176  ) const;
1177 
1200  SI_Error AddEntry(
1201  const SI_CHAR *a_pSection,
1202  const SI_CHAR *a_pKey,
1203  const SI_CHAR *a_pValue,
1204  const SI_CHAR *a_pComment,
1205  bool a_bForceReplace,
1206  bool a_bCopyStrings
1207  );
1208 
1210  inline bool IsSpace(SI_CHAR ch) const {
1211  return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
1212  }
1213 
1215  inline bool IsComment(SI_CHAR ch) const {
1216  return (ch == ';' || ch == '#');
1217  }
1218 
1219 
1221  inline void SkipNewLine(SI_CHAR *&a_pData) const {
1222  a_pData += (*a_pData == '\r' && *(a_pData + 1) == '\n') ? 2 : 1;
1223  }
1224 
1226  SI_Error CopyString(const SI_CHAR *&a_pString);
1227 
1229  void DeleteString(const SI_CHAR *a_pString);
1230 
1232  bool IsLess(const SI_CHAR *a_pLeft, const SI_CHAR *a_pRight) const {
1233  const static SI_STRLESS isLess = SI_STRLESS();
1234  return isLess(a_pLeft, a_pRight);
1235  }
1236 
1237  bool IsMultiLineTag(const SI_CHAR *a_pData) const;
1238  bool IsMultiLineData(const SI_CHAR *a_pData) const;
1239  bool LoadMultiLineText(
1240  SI_CHAR *&a_pData,
1241  const SI_CHAR *&a_pVal,
1242  const SI_CHAR *a_pTagName,
1243  bool a_bAllowBlankLinesInComment = false
1244  ) const;
1245  bool IsNewLineChar(SI_CHAR a_c) const;
1246 
1247  bool OutputMultiLineText(
1248  OutputWriter &a_oOutput,
1249  Converter &a_oConverter,
1250  const SI_CHAR *a_pText
1251  ) const;
1252 
1253  private:
1259  SI_CHAR *m_pData;
1260 
1265  size_t m_uDataLen;
1266 
1268  const SI_CHAR *m_pFileComment;
1269 
1271  TSection m_data;
1272 
1277  TNamesDepend m_strings;
1278 
1280  bool m_bStoreIsUtf8;
1281 
1283  bool m_bAllowMultiKey;
1284 
1286  bool m_bAllowMultiLine;
1287 
1289  bool m_bSpaces;
1290 
1294  int m_nOrder;
1295 };
1296 
1297 // ---------------------------------------------------------------------------
1298 // IMPLEMENTATION
1299 // ---------------------------------------------------------------------------
1300 
1301 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1303  bool a_bIsUtf8,
1304  bool a_bAllowMultiKey,
1305  bool a_bAllowMultiLine
1306 )
1307  : m_pData(0)
1308  , m_uDataLen(0)
1309  , m_pFileComment(NULL)
1310  , m_bStoreIsUtf8(a_bIsUtf8)
1311  , m_bAllowMultiKey(a_bAllowMultiKey)
1312  , m_bAllowMultiLine(a_bAllowMultiLine)
1313  , m_bSpaces(true)
1314  , m_nOrder(0)
1315 { }
1316 
1317 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1319  Reset();
1320 }
1321 
1322 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1323 void
1325  // remove all data
1326  delete[] m_pData;
1327  m_pData = NULL;
1328  m_uDataLen = 0;
1329  m_pFileComment = NULL;
1330 
1331  if (!m_data.empty()) {
1332  m_data.erase(m_data.begin(), m_data.end());
1333  }
1334 
1335  // remove all strings
1336  if (!m_strings.empty()) {
1337  typename TNamesDepend::iterator i = m_strings.begin();
1338 
1339  for (; i != m_strings.end(); ++i) {
1340  delete[] const_cast<SI_CHAR *>(i->pItem);
1341  }
1342 
1343  m_strings.erase(m_strings.begin(), m_strings.end());
1344  }
1345 }
1346 
1347 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1348 SI_Error
1350  const char *a_pszFile
1351 ) {
1352  FILE *fp = NULL;
1353 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1354  fopen_s(&fp, a_pszFile, "rb");
1355 #else // !__STDC_WANT_SECURE_LIB__
1356  fp = fopen(a_pszFile, "rb");
1357 #endif // __STDC_WANT_SECURE_LIB__
1358 
1359  if (!fp) {
1360  return SI_FILE;
1361  }
1362 
1363  SI_Error rc = LoadFile(fp);
1364  fclose(fp);
1365  return rc;
1366 }
1367 
1368 #ifdef SI_HAS_WIDE_FILE
1369 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1370 SI_Error
1372  const SI_WCHAR_T *a_pwszFile
1373 ) {
1374 #ifdef _WIN32
1375  FILE *fp = NULL;
1376 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1377  _wfopen_s(&fp, a_pwszFile, L"rb");
1378 #else // !__STDC_WANT_SECURE_LIB__
1379  fp = _wfopen(a_pwszFile, L"rb");
1380 #endif // __STDC_WANT_SECURE_LIB__
1381 
1382  if (!fp) {
1383  return SI_FILE;
1384  }
1385 
1386  SI_Error rc = LoadFile(fp);
1387  fclose(fp);
1388  return rc;
1389 #else // !_WIN32 (therefore SI_CONVERT_ICU)
1390  char szFile[256];
1391  u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
1392  return LoadFile(szFile);
1393 #endif // _WIN32
1394 }
1395 #endif // SI_HAS_WIDE_FILE
1396 
1397 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1398 SI_Error
1400  FILE *a_fpFile
1401 ) {
1402  // load the raw file data
1403  int retval = fseek(a_fpFile, 0, SEEK_END);
1404 
1405  if (retval != 0) {
1406  return SI_FILE;
1407  }
1408 
1409  long lSize = ftell(a_fpFile);
1410 
1411  if (lSize < 0) {
1412  return SI_FILE;
1413  }
1414 
1415  if (lSize == 0) {
1416  return SI_OK;
1417  }
1418 
1419  // allocate and ensure NULL terminated
1420  char *pData = new (std::nothrow) char[lSize + 1];
1421 
1422  if (!pData) {
1423  return SI_NOMEM;
1424  }
1425 
1426  pData[lSize] = 0;
1427 
1428  // load data into buffer
1429  fseek(a_fpFile, 0, SEEK_SET);
1430  size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
1431 
1432  if (uRead != (size_t) lSize) {
1433  delete[] pData;
1434  return SI_FILE;
1435  }
1436 
1437  // convert the raw data to unicode
1438  SI_Error rc = LoadData(pData, uRead);
1439  delete[] pData;
1440  return rc;
1441 }
1442 
1443 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1444 SI_Error
1446  const char *a_pData,
1447  size_t a_uDataLen
1448 ) {
1449  if (!a_pData) {
1450  return SI_OK;
1451  }
1452 
1453  // if the UTF-8 BOM exists, consume it and set mode to unicode, if we have
1454  // already loaded data and try to change mode half-way through then this will
1455  // be ignored and we will assert in debug versions
1456  if (a_uDataLen >= 3 && memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
1457  a_pData += 3;
1458  a_uDataLen -= 3;
1459  SI_ASSERT(m_bStoreIsUtf8 || !m_pData); // we don't expect mixed mode data
1460  SetUnicode();
1461  }
1462 
1463  if (a_uDataLen == 0) {
1464  return SI_OK;
1465  }
1466 
1467  // determine the length of the converted data
1468  SI_CONVERTER converter(m_bStoreIsUtf8);
1469  size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
1470 
1471  if (uLen == (size_t)(-1)) {
1472  return SI_FAIL;
1473  }
1474 
1475  // allocate memory for the data, ensure that there is a NULL
1476  // terminator wherever the converted data ends
1477  SI_CHAR *pData = new (std::nothrow) SI_CHAR[uLen + 1];
1478 
1479  if (!pData) {
1480  return SI_NOMEM;
1481  }
1482 
1483  memset(pData, 0, sizeof(SI_CHAR) * (uLen + 1));
1484 
1485  // convert the data
1486  if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
1487  delete[] pData;
1488  return SI_FAIL;
1489  }
1490 
1491  // parse it
1492  const static SI_CHAR empty = 0;
1493  SI_CHAR *pWork = pData;
1494  const SI_CHAR *pSection = &empty;
1495  const SI_CHAR *pItem = NULL;
1496  const SI_CHAR *pVal = NULL;
1497  const SI_CHAR *pComment = NULL;
1498 
1499  // We copy the strings if we are loading data into this class when we
1500  // already have stored some.
1501  bool bCopyStrings = (m_pData != NULL);
1502 
1503  // find a file comment if it exists, this is a comment that starts at the
1504  // beginning of the file and continues until the first blank line.
1505  SI_Error rc = FindFileComment(pWork, bCopyStrings);
1506 
1507  if (rc < 0) {
1508  return rc;
1509  }
1510 
1511  // add every entry in the file to the data table
1512  while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
1513  rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings);
1514 
1515  if (rc < 0) {
1516  return rc;
1517  }
1518  }
1519 
1520  // store these strings if we didn't copy them
1521  if (bCopyStrings) {
1522  delete[] pData;
1523  } else {
1524  m_pData = pData;
1525  m_uDataLen = uLen + 1;
1526  }
1527 
1528  return SI_OK;
1529 }
1530 
1531 #ifdef SI_SUPPORT_IOSTREAMS
1532 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1533 SI_Error
1535  std::istream &a_istream
1536 ) {
1537  std::string strData;
1538  char szBuf[512];
1539 
1540  do {
1541  a_istream.get(szBuf, sizeof(szBuf), '\0');
1542  strData.append(szBuf);
1543  } while (a_istream.good());
1544 
1545  return LoadData(strData);
1546 }
1547 #endif // SI_SUPPORT_IOSTREAMS
1548 
1549 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1550 SI_Error
1552  SI_CHAR *&a_pData,
1553  bool a_bCopyStrings
1554 ) {
1555  // there can only be a single file comment
1556  if (m_pFileComment) {
1557  return SI_OK;
1558  }
1559 
1560  // Load the file comment as multi-line text, this will modify all of
1561  // the newline characters to be single \n chars
1562  if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
1563  return SI_OK;
1564  }
1565 
1566  // copy the string if necessary
1567  if (a_bCopyStrings) {
1568  SI_Error rc = CopyString(m_pFileComment);
1569 
1570  if (rc < 0) {
1571  return rc;
1572  }
1573  }
1574 
1575  return SI_OK;
1576 }
1577 
1578 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1579 bool
1581  SI_CHAR *&a_pData,
1582  const SI_CHAR *&a_pSection,
1583  const SI_CHAR *&a_pKey,
1584  const SI_CHAR *&a_pVal,
1585  const SI_CHAR *&a_pComment
1586 ) const {
1587  a_pComment = NULL;
1588 
1589  SI_CHAR *pTrail = NULL;
1590 
1591  while (*a_pData) {
1592  // skip spaces and empty lines
1593  while (*a_pData && IsSpace(*a_pData)) {
1594  ++a_pData;
1595  }
1596 
1597  if (!*a_pData) {
1598  break;
1599  }
1600 
1601  // skip processing of comment lines but keep a pointer to
1602  // the start of the comment.
1603  if (IsComment(*a_pData)) {
1604  LoadMultiLineText(a_pData, a_pComment, NULL, true);
1605  continue;
1606  }
1607 
1608  // process section names
1609  if (*a_pData == '[') {
1610  // skip leading spaces
1611  ++a_pData;
1612 
1613  while (*a_pData && IsSpace(*a_pData)) {
1614  ++a_pData;
1615  }
1616 
1617  // find the end of the section name (it may contain spaces)
1618  // and convert it to lowercase as necessary
1619  a_pSection = a_pData;
1620 
1621  while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
1622  ++a_pData;
1623  }
1624 
1625  // if it's an invalid line, just skip it
1626  if (*a_pData != ']') {
1627  continue;
1628  }
1629 
1630  // remove trailing spaces from the section
1631  pTrail = a_pData - 1;
1632 
1633  while (pTrail >= a_pSection && IsSpace(*pTrail)) {
1634  --pTrail;
1635  }
1636 
1637  ++pTrail;
1638  *pTrail = 0;
1639 
1640  // skip to the end of the line
1641  ++a_pData; // safe as checked that it == ']' above
1642 
1643  while (*a_pData && !IsNewLineChar(*a_pData)) {
1644  ++a_pData;
1645  }
1646 
1647  a_pKey = NULL;
1648  a_pVal = NULL;
1649  return true;
1650  }
1651 
1652  // find the end of the key name (it may contain spaces)
1653  // and convert it to lowercase as necessary
1654  a_pKey = a_pData;
1655 
1656  while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
1657  ++a_pData;
1658  }
1659 
1660  // if it's an invalid line, just skip it
1661  if (*a_pData != '=') {
1662  continue;
1663  }
1664 
1665  // empty keys are invalid
1666  if (a_pKey == a_pData) {
1667  while (*a_pData && !IsNewLineChar(*a_pData)) {
1668  ++a_pData;
1669  }
1670 
1671  continue;
1672  }
1673 
1674  // remove trailing spaces from the key
1675  pTrail = a_pData - 1;
1676 
1677  while (pTrail >= a_pKey && IsSpace(*pTrail)) {
1678  --pTrail;
1679  }
1680 
1681  ++pTrail;
1682  *pTrail = 0;
1683 
1684  // skip leading whitespace on the value
1685  ++a_pData; // safe as checked that it == '=' above
1686 
1687  while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
1688  ++a_pData;
1689  }
1690 
1691  // find the end of the value which is the end of this line
1692  a_pVal = a_pData;
1693 
1694  while (*a_pData && !IsNewLineChar(*a_pData)) {
1695  ++a_pData;
1696  }
1697 
1698  // remove trailing spaces from the value
1699  pTrail = a_pData - 1;
1700 
1701  if (*a_pData) { // prepare for the next round
1702  SkipNewLine(a_pData);
1703  }
1704 
1705  while (pTrail >= a_pVal && IsSpace(*pTrail)) {
1706  --pTrail;
1707  }
1708 
1709  ++pTrail;
1710  *pTrail = 0;
1711 
1712  // check for multi-line entries
1713  if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
1714  // skip the "<<<" to get the tag that will end the multiline
1715  const SI_CHAR *pTagName = a_pVal + 3;
1716  return LoadMultiLineText(a_pData, a_pVal, pTagName);
1717  }
1718 
1719  // return the standard entry
1720  return true;
1721  }
1722 
1723  return false;
1724 }
1725 
1726 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1727 bool
1729  const SI_CHAR *a_pVal
1730 ) const {
1731  // check for the "<<<" prefix for a multi-line entry
1732  if (*a_pVal++ != '<') {
1733  return false;
1734  }
1735 
1736  if (*a_pVal++ != '<') {
1737  return false;
1738  }
1739 
1740  if (*a_pVal++ != '<') {
1741  return false;
1742  }
1743 
1744  return true;
1745 }
1746 
1747 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1748 bool
1750  const SI_CHAR *a_pData
1751 ) const {
1752  // data is multi-line if it has any of the following features:
1753  // * whitespace prefix
1754  // * embedded newlines
1755  // * whitespace suffix
1756 
1757  // empty string
1758  if (!*a_pData) {
1759  return false;
1760  }
1761 
1762  // check for prefix
1763  if (IsSpace(*a_pData)) {
1764  return true;
1765  }
1766 
1767  // embedded newlines
1768  while (*a_pData) {
1769  if (IsNewLineChar(*a_pData)) {
1770  return true;
1771  }
1772 
1773  ++a_pData;
1774  }
1775 
1776  // check for suffix
1777  if (IsSpace(*--a_pData)) {
1778  return true;
1779  }
1780 
1781  return false;
1782 }
1783 
1784 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1785 bool
1787  SI_CHAR a_c
1788 ) const {
1789  return (a_c == '\n' || a_c == '\r');
1790 }
1791 
1792 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1793 bool
1795  SI_CHAR *&a_pData,
1796  const SI_CHAR *&a_pVal,
1797  const SI_CHAR *a_pTagName,
1798  bool a_bAllowBlankLinesInComment
1799 ) const {
1800  // we modify this data to strip all newlines down to a single '\n'
1801  // character. This means that on Windows we need to strip out some
1802  // characters which will make the data shorter.
1803  // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
1804  // LINE1-LINE1\nLINE2-LINE2\0
1805  // The pDataLine entry is the pointer to the location in memory that
1806  // the current line needs to start to run following the existing one.
1807  // This may be the same as pCurrLine in which case no move is needed.
1808  SI_CHAR *pDataLine = a_pData;
1809  SI_CHAR *pCurrLine;
1810 
1811  // value starts at the current line
1812  a_pVal = a_pData;
1813 
1814  // find the end tag. This tag must start in column 1 and be
1815  // followed by a newline. No whitespace removal is done while
1816  // searching for this tag.
1817  SI_CHAR cEndOfLineChar = *a_pData;
1818 
1819  for (;;) {
1820  // if we are loading comments then we need a comment character as
1821  // the first character on every line
1822  if (!a_pTagName && !IsComment(*a_pData)) {
1823  // if we aren't allowing blank lines then we're done
1824  if (!a_bAllowBlankLinesInComment) {
1825  break;
1826  }
1827 
1828  // if we are allowing blank lines then we only include them
1829  // in this comment if another comment follows, so read ahead
1830  // to find out.
1831  SI_CHAR *pCurr = a_pData;
1832  int nNewLines = 0;
1833 
1834  while (IsSpace(*pCurr)) {
1835  if (IsNewLineChar(*pCurr)) {
1836  ++nNewLines;
1837  SkipNewLine(pCurr);
1838  } else {
1839  ++pCurr;
1840  }
1841  }
1842 
1843  // we have a comment, add the blank lines to the output
1844  // and continue processing from here
1845  if (IsComment(*pCurr)) {
1846  for (; nNewLines > 0; --nNewLines) {
1847  *pDataLine++ = '\n';
1848  }
1849 
1850  a_pData = pCurr;
1851  continue;
1852  }
1853 
1854  // the comment ends here
1855  break;
1856  }
1857 
1858  // find the end of this line
1859  pCurrLine = a_pData;
1860 
1861  while (*a_pData && !IsNewLineChar(*a_pData)) {
1862  ++a_pData;
1863  }
1864 
1865  // move this line down to the location that it should be if necessary
1866  if (pDataLine < pCurrLine) {
1867  size_t nLen = (size_t)(a_pData - pCurrLine);
1868  memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
1869  pDataLine[nLen] = '\0';
1870  }
1871 
1872  // end the line with a NULL
1873  cEndOfLineChar = *a_pData;
1874  *a_pData = 0;
1875 
1876  // if are looking for a tag then do the check now. This is done before
1877  // checking for end of the data, so that if we have the tag at the end
1878  // of the data then the tag is removed correctly.
1879  if (a_pTagName &&
1880  (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine))) {
1881  break;
1882  }
1883 
1884  // if we are at the end of the data then we just automatically end
1885  // this entry and return the current data.
1886  if (!cEndOfLineChar) {
1887  return true;
1888  }
1889 
1890  // otherwise we need to process this newline to ensure that it consists
1891  // of just a single \n character.
1892  pDataLine += (a_pData - pCurrLine);
1893  *a_pData = cEndOfLineChar;
1894  SkipNewLine(a_pData);
1895  *pDataLine++ = '\n';
1896  }
1897 
1898  // if we didn't find a comment at all then return false
1899  if (a_pVal == a_pData) {
1900  a_pVal = NULL;
1901  return false;
1902  }
1903 
1904  // the data (which ends at the end of the last line) needs to be
1905  // null-terminated BEFORE before the newline character(s). If the
1906  // user wants a new line in the multi-line data then they need to
1907  // add an empty line before the tag.
1908  *--pDataLine = '\0';
1909 
1910  // if looking for a tag and if we aren't at the end of the data,
1911  // then move a_pData to the start of the next line.
1912  if (a_pTagName && cEndOfLineChar) {
1913  SI_ASSERT(IsNewLineChar(cEndOfLineChar));
1914  *a_pData = cEndOfLineChar;
1915  SkipNewLine(a_pData);
1916  }
1917 
1918  return true;
1919 }
1920 
1921 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1922 SI_Error
1924  const SI_CHAR *&a_pString
1925 ) {
1926  size_t uLen = 0;
1927 
1928  if (sizeof(SI_CHAR) == sizeof(char)) {
1929  uLen = strlen((const char *)a_pString);
1930  } else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
1931  uLen = wcslen((const wchar_t *)a_pString);
1932  } else {
1933  for (; a_pString[uLen]; ++uLen) /*loop*/ ;
1934  }
1935 
1936  ++uLen; // NULL character
1937  SI_CHAR *pCopy = new (std::nothrow) SI_CHAR[uLen];
1938 
1939  if (!pCopy) {
1940  return SI_NOMEM;
1941  }
1942 
1943  memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
1944  m_strings.push_back(pCopy);
1945  a_pString = pCopy;
1946  return SI_OK;
1947 }
1948 
1949 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1950 SI_Error
1952  const SI_CHAR *a_pSection,
1953  const SI_CHAR *a_pKey,
1954  const SI_CHAR *a_pValue,
1955  const SI_CHAR *a_pComment,
1956  bool a_bForceReplace,
1957  bool a_bCopyStrings
1958 ) {
1959  SI_Error rc;
1960  bool bInserted = false;
1961 
1962  SI_ASSERT(!a_pComment || IsComment(*a_pComment));
1963 
1964  // if we are copying strings then make a copy of the comment now
1965  // because we will need it when we add the entry.
1966  if (a_bCopyStrings && a_pComment) {
1967  rc = CopyString(a_pComment);
1968 
1969  if (rc < 0) {
1970  return rc;
1971  }
1972  }
1973 
1974  // create the section entry if necessary
1975  typename TSection::iterator iSection = m_data.find(a_pSection);
1976 
1977  if (iSection == m_data.end()) {
1978  // if the section doesn't exist then we need a copy as the
1979  // string needs to last beyond the end of this function
1980  if (a_bCopyStrings) {
1981  rc = CopyString(a_pSection);
1982 
1983  if (rc < 0) {
1984  return rc;
1985  }
1986  }
1987 
1988  // only set the comment if this is a section only entry
1989  Entry oSection(a_pSection, ++m_nOrder);
1990 
1991  if (a_pComment && (!a_pKey || !a_pValue)) {
1992  oSection.pComment = a_pComment;
1993  }
1994 
1995  typename TSection::value_type oEntry(oSection, TKeyVal());
1996  typedef typename TSection::iterator SectionIterator;
1997  std::pair<SectionIterator, bool> i = m_data.insert(oEntry);
1998  iSection = i.first;
1999  bInserted = true;
2000  }
2001 
2002  if (!a_pKey || !a_pValue) {
2003  // section only entries are specified with pItem and pVal as NULL
2004  return bInserted ? SI_INSERTED : SI_UPDATED;
2005  }
2006 
2007  // check for existence of the key
2008  TKeyVal &keyval = iSection->second;
2009  typename TKeyVal::iterator iKey = keyval.find(a_pKey);
2010 
2011  // remove all existing entries but save the load order and
2012  // comment of the first entry
2013  int nLoadOrder = ++m_nOrder;
2014 
2015  if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) {
2016  const SI_CHAR *pComment = NULL;
2017 
2018  while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) {
2019  if (iKey->first.nOrder < nLoadOrder) {
2020  nLoadOrder = iKey->first.nOrder;
2021  pComment = iKey->first.pComment;
2022  }
2023 
2024  ++iKey;
2025  }
2026 
2027  if (pComment) {
2028  DeleteString(a_pComment);
2029  a_pComment = pComment;
2030  CopyString(a_pComment);
2031  }
2032 
2033  Delete(a_pSection, a_pKey);
2034  iKey = keyval.end();
2035  }
2036 
2037  // make string copies if necessary
2038  bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
2039 
2040  if (a_bCopyStrings) {
2041  if (bForceCreateNewKey || iKey == keyval.end()) {
2042  // if the key doesn't exist then we need a copy as the
2043  // string needs to last beyond the end of this function
2044  // because we will be inserting the key next
2045  rc = CopyString(a_pKey);
2046 
2047  if (rc < 0) {
2048  return rc;
2049  }
2050  }
2051 
2052  // we always need a copy of the value
2053  rc = CopyString(a_pValue);
2054 
2055  if (rc < 0) {
2056  return rc;
2057  }
2058  }
2059 
2060  // create the key entry
2061  if (iKey == keyval.end() || bForceCreateNewKey) {
2062  Entry oKey(a_pKey, nLoadOrder);
2063 
2064  if (a_pComment) {
2065  oKey.pComment = a_pComment;
2066  }
2067 
2068  typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL));
2069  iKey = keyval.insert(oEntry);
2070  bInserted = true;
2071  }
2072 
2073  iKey->second = a_pValue;
2074  return bInserted ? SI_INSERTED : SI_UPDATED;
2075 }
2076 
2077 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2078 const SI_CHAR *
2080  const SI_CHAR *a_pSection,
2081  const SI_CHAR *a_pKey,
2082  const SI_CHAR *a_pDefault,
2083  bool *a_pHasMultiple
2084 ) const {
2085  if (a_pHasMultiple) {
2086  *a_pHasMultiple = false;
2087  }
2088 
2089  if (!a_pSection || !a_pKey) {
2090  return a_pDefault;
2091  }
2092 
2093  typename TSection::const_iterator iSection = m_data.find(a_pSection);
2094 
2095  if (iSection == m_data.end()) {
2096  return a_pDefault;
2097  }
2098 
2099  typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2100 
2101  if (iKeyVal == iSection->second.end()) {
2102  return a_pDefault;
2103  }
2104 
2105  // check for multiple entries with the same key
2106  if (m_bAllowMultiKey && a_pHasMultiple) {
2107  typename TKeyVal::const_iterator iTemp = iKeyVal;
2108 
2109  if (++iTemp != iSection->second.end()) {
2110  if (!IsLess(a_pKey, iTemp->first.pItem)) {
2111  *a_pHasMultiple = true;
2112  }
2113  }
2114  }
2115 
2116  return iKeyVal->second;
2117 }
2118 
2119 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2120 long
2122  const SI_CHAR *a_pSection,
2123  const SI_CHAR *a_pKey,
2124  long a_nDefault,
2125  bool *a_pHasMultiple
2126 ) const {
2127  // return the default if we don't have a value
2128  const SI_CHAR *pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2129 
2130  if (!pszValue || !*pszValue) {
2131  return a_nDefault;
2132  }
2133 
2134  // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2135  char szValue[64] = { 0 };
2136  SI_CONVERTER c(m_bStoreIsUtf8);
2137 
2138  if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2139  return a_nDefault;
2140  }
2141 
2142  // handle the value as hex if prefaced with "0x"
2143  long nValue = a_nDefault;
2144  char *pszSuffix = szValue;
2145 
2146  if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
2147  if (!szValue[2]) {
2148  return a_nDefault;
2149  }
2150 
2151  nValue = strtol(&szValue[2], &pszSuffix, 16);
2152  } else {
2153  nValue = strtol(szValue, &pszSuffix, 10);
2154  }
2155 
2156  // any invalid strings will return the default value
2157  if (*pszSuffix) {
2158  return a_nDefault;
2159  }
2160 
2161  return nValue;
2162 }
2163 
2164 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2165 SI_Error
2167  const SI_CHAR *a_pSection,
2168  const SI_CHAR *a_pKey,
2169  long a_nValue,
2170  const SI_CHAR *a_pComment,
2171  bool a_bUseHex,
2172  bool a_bForceReplace
2173 ) {
2174  // use SetValue to create sections
2175  if (!a_pSection || !a_pKey) {
2176  return SI_FAIL;
2177  }
2178 
2179  // convert to an ASCII string
2180  char szInput[64];
2181 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2182  sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2183 #else // !__STDC_WANT_SECURE_LIB__
2184  sprintf(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2185 #endif // __STDC_WANT_SECURE_LIB__
2186 
2187  // convert to output text
2188  SI_CHAR szOutput[64];
2189  SI_CONVERTER c(m_bStoreIsUtf8);
2190  c.ConvertFromStore(szInput, strlen(szInput) + 1,
2191  szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2192 
2193  // actually add it
2194  return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2195 }
2196 
2197 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2198 double
2200  const SI_CHAR *a_pSection,
2201  const SI_CHAR *a_pKey,
2202  double a_nDefault,
2203  bool *a_pHasMultiple
2204 ) const {
2205  // return the default if we don't have a value
2206  const SI_CHAR *pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2207 
2208  if (!pszValue || !*pszValue) {
2209  return a_nDefault;
2210  }
2211 
2212  // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2213  char szValue[64] = { 0 };
2214  SI_CONVERTER c(m_bStoreIsUtf8);
2215 
2216  if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2217  return a_nDefault;
2218  }
2219 
2220  char *pszSuffix = NULL;
2221  double nValue = strtod(szValue, &pszSuffix);
2222 
2223  // any invalid strings will return the default value
2224  if (!pszSuffix || *pszSuffix) {
2225  return a_nDefault;
2226  }
2227 
2228  return nValue;
2229 }
2230 
2231 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2232 SI_Error
2234  const SI_CHAR *a_pSection,
2235  const SI_CHAR *a_pKey,
2236  double a_nValue,
2237  const SI_CHAR *a_pComment,
2238  bool a_bForceReplace
2239 ) {
2240  // use SetValue to create sections
2241  if (!a_pSection || !a_pKey) {
2242  return SI_FAIL;
2243  }
2244 
2245  // convert to an ASCII string
2246  char szInput[64];
2247 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2248  sprintf_s(szInput, "%f", a_nValue);
2249 #else // !__STDC_WANT_SECURE_LIB__
2250  sprintf(szInput, "%f", a_nValue);
2251 #endif // __STDC_WANT_SECURE_LIB__
2252 
2253  // convert to output text
2254  SI_CHAR szOutput[64];
2255  SI_CONVERTER c(m_bStoreIsUtf8);
2256  c.ConvertFromStore(szInput, strlen(szInput) + 1,
2257  szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2258 
2259  // actually add it
2260  return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2261 }
2262 
2263 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2264 bool
2266  const SI_CHAR *a_pSection,
2267  const SI_CHAR *a_pKey,
2268  bool a_bDefault,
2269  bool *a_pHasMultiple
2270 ) 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 
2274  if (!pszValue || !*pszValue) {
2275  return a_bDefault;
2276  }
2277 
2278  // we only look at the minimum number of characters
2279  switch (pszValue[0]) {
2280  case 't':
2281  case 'T': // true
2282  case 'y':
2283  case 'Y': // yes
2284  case '1': // 1 (one)
2285  return true;
2286 
2287  case 'f':
2288  case 'F': // false
2289  case 'n':
2290  case 'N': // no
2291  case '0': // 0 (zero)
2292  return false;
2293 
2294  case 'o':
2295  case 'O':
2296  if (pszValue[1] == 'n' || pszValue[1] == 'N') {
2297  return true; // on
2298  }
2299 
2300  if (pszValue[1] == 'f' || pszValue[1] == 'F') {
2301  return false; // off
2302  }
2303 
2304  break;
2305  }
2306 
2307  // no recognized value, return the default
2308  return a_bDefault;
2309 }
2310 
2311 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2312 SI_Error
2314  const SI_CHAR *a_pSection,
2315  const SI_CHAR *a_pKey,
2316  bool a_bValue,
2317  const SI_CHAR *a_pComment,
2318  bool a_bForceReplace
2319 ) {
2320  // use SetValue to create sections
2321  if (!a_pSection || !a_pKey) {
2322  return SI_FAIL;
2323  }
2324 
2325  // convert to an ASCII string
2326  const char *pszInput = a_bValue ? "true" : "false";
2327 
2328  // convert to output text
2329  SI_CHAR szOutput[64];
2330  SI_CONVERTER c(m_bStoreIsUtf8);
2331  c.ConvertFromStore(pszInput, strlen(pszInput) + 1,
2332  szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2333 
2334  // actually add it
2335  return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2336 }
2337 
2338 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2339 bool
2341  const SI_CHAR *a_pSection,
2342  const SI_CHAR *a_pKey,
2343  TNamesDepend &a_values
2344 ) const {
2345  a_values.clear();
2346 
2347  if (!a_pSection || !a_pKey) {
2348  return false;
2349  }
2350 
2351  typename TSection::const_iterator iSection = m_data.find(a_pSection);
2352 
2353  if (iSection == m_data.end()) {
2354  return false;
2355  }
2356 
2357  typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2358 
2359  if (iKeyVal == iSection->second.end()) {
2360  return false;
2361  }
2362 
2363  // insert all values for this key
2364  a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2365 
2366  if (m_bAllowMultiKey) {
2367  ++iKeyVal;
2368 
2369  while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {
2370  a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2371  ++iKeyVal;
2372  }
2373  }
2374 
2375  return true;
2376 }
2377 
2378 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2379 int
2381  const SI_CHAR *a_pSection
2382 ) const {
2383  if (!a_pSection) {
2384  return -1;
2385  }
2386 
2387  typename TSection::const_iterator iSection = m_data.find(a_pSection);
2388 
2389  if (iSection == m_data.end()) {
2390  return -1;
2391  }
2392 
2393  const TKeyVal &section = iSection->second;
2394 
2395  // if multi-key isn't permitted then the section size is
2396  // the number of keys that we have.
2397  if (!m_bAllowMultiKey || section.empty()) {
2398  return (int) section.size();
2399  }
2400 
2401  // otherwise we need to count them
2402  int nCount = 0;
2403  const SI_CHAR *pLastKey = NULL;
2404  typename TKeyVal::const_iterator iKeyVal = section.begin();
2405 
2406  for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
2407  if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2408  ++nCount;
2409  pLastKey = iKeyVal->first.pItem;
2410  }
2411  }
2412 
2413  return nCount;
2414 }
2415 
2416 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2419  const SI_CHAR *a_pSection
2420 ) const {
2421  if (a_pSection) {
2422  typename TSection::const_iterator i = m_data.find(a_pSection);
2423 
2424  if (i != m_data.end()) {
2425  return &(i->second);
2426  }
2427  }
2428 
2429  return 0;
2430 }
2431 
2432 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2433 void
2435  TNamesDepend &a_names
2436 ) const {
2437  a_names.clear();
2438  typename TSection::const_iterator i = m_data.begin();
2439 
2440  for (int n = 0; i != m_data.end(); ++i, ++n) {
2441  a_names.push_back(i->first);
2442  }
2443 }
2444 
2445 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2446 bool
2448  const SI_CHAR *a_pSection,
2449  TNamesDepend &a_names
2450 ) const {
2451  a_names.clear();
2452 
2453  if (!a_pSection) {
2454  return false;
2455  }
2456 
2457  typename TSection::const_iterator iSection = m_data.find(a_pSection);
2458 
2459  if (iSection == m_data.end()) {
2460  return false;
2461  }
2462 
2463  const TKeyVal &section = iSection->second;
2464  const SI_CHAR *pLastKey = NULL;
2465  typename TKeyVal::const_iterator iKeyVal = section.begin();
2466 
2467  for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
2468  if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2469  a_names.push_back(iKeyVal->first);
2470  pLastKey = iKeyVal->first.pItem;
2471  }
2472  }
2473 
2474  return true;
2475 }
2476 
2477 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2478 SI_Error
2480  const char *a_pszFile,
2481  bool a_bAddSignature
2482 ) const {
2483  FILE *fp = NULL;
2484 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2485  fopen_s(&fp, a_pszFile, "wb");
2486 #else // !__STDC_WANT_SECURE_LIB__
2487  fp = fopen(a_pszFile, "wb");
2488 #endif // __STDC_WANT_SECURE_LIB__
2489 
2490  if (!fp) {
2491  return SI_FILE;
2492  }
2493 
2494  SI_Error rc = SaveFile(fp, a_bAddSignature);
2495  fclose(fp);
2496  return rc;
2497 }
2498 
2499 #ifdef SI_HAS_WIDE_FILE
2500 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2501 SI_Error
2503  const SI_WCHAR_T *a_pwszFile,
2504  bool a_bAddSignature
2505 ) const {
2506 #ifdef _WIN32
2507  FILE *fp = NULL;
2508 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2509  _wfopen_s(&fp, a_pwszFile, L"wb");
2510 #else // !__STDC_WANT_SECURE_LIB__
2511  fp = _wfopen(a_pwszFile, L"wb");
2512 #endif // __STDC_WANT_SECURE_LIB__
2513 
2514  if (!fp) {
2515  return SI_FILE;
2516  }
2517 
2518  SI_Error rc = SaveFile(fp, a_bAddSignature);
2519  fclose(fp);
2520  return rc;
2521 #else // !_WIN32 (therefore SI_CONVERT_ICU)
2522  char szFile[256];
2523  u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
2524  return SaveFile(szFile, a_bAddSignature);
2525 #endif // _WIN32
2526 }
2527 #endif // SI_HAS_WIDE_FILE
2528 
2529 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2530 SI_Error
2532  FILE *a_pFile,
2533  bool a_bAddSignature
2534 ) const {
2535  FileWriter writer(a_pFile);
2536  return Save(writer, a_bAddSignature);
2537 }
2538 
2539 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2540 SI_Error
2542  OutputWriter &a_oOutput,
2543  bool a_bAddSignature
2544 ) const {
2545  Converter convert(m_bStoreIsUtf8);
2546 
2547  // add the UTF-8 signature if it is desired
2548  if (m_bStoreIsUtf8 && a_bAddSignature) {
2549  a_oOutput.Write(SI_UTF8_SIGNATURE);
2550  }
2551 
2552  // get all of the sections sorted in load order
2553  TNamesDepend oSections;
2554  GetAllSections(oSections);
2555 #if defined(_MSC_VER) && _MSC_VER <= 1200
2556  oSections.sort();
2557 #elif defined(__BORLANDC__)
2558  oSections.sort(Entry::LoadOrder());
2559 #else
2560  oSections.sort(typename Entry::LoadOrder());
2561 #endif
2562 
2563  // write the file comment if we have one
2564  bool bNeedNewLine = false;
2565 
2566  if (m_pFileComment) {
2567  if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
2568  return SI_FAIL;
2569  }
2570 
2571  bNeedNewLine = true;
2572  }
2573 
2574  // iterate through our sections and output the data
2575  typename TNamesDepend::const_iterator iSection = oSections.begin();
2576 
2577  for (; iSection != oSections.end(); ++iSection) {
2578  // write out the comment if there is one
2579  if (iSection->pComment) {
2580  if (bNeedNewLine) {
2581  a_oOutput.Write(SI_NEWLINE_A);
2582  a_oOutput.Write(SI_NEWLINE_A);
2583  }
2584 
2585  if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) {
2586  return SI_FAIL;
2587  }
2588 
2589  bNeedNewLine = false;
2590  }
2591 
2592  if (bNeedNewLine) {
2593  a_oOutput.Write(SI_NEWLINE_A);
2594  a_oOutput.Write(SI_NEWLINE_A);
2595  bNeedNewLine = false;
2596  }
2597 
2598  // write the section (unless there is no section name)
2599  if (*iSection->pItem) {
2600  if (!convert.ConvertToStore(iSection->pItem)) {
2601  return SI_FAIL;
2602  }
2603 
2604  a_oOutput.Write("[");
2605  a_oOutput.Write(convert.Data());
2606  a_oOutput.Write("]");
2607  a_oOutput.Write(SI_NEWLINE_A);
2608  }
2609 
2610  // get all of the keys sorted in load order
2611  TNamesDepend oKeys;
2612  GetAllKeys(iSection->pItem, oKeys);
2613 #if defined(_MSC_VER) && _MSC_VER <= 1200
2614  oKeys.sort();
2615 #elif defined(__BORLANDC__)
2616  oKeys.sort(Entry::LoadOrder());
2617 #else
2618  oKeys.sort(typename Entry::LoadOrder());
2619 #endif
2620 
2621  // write all keys and values
2622  typename TNamesDepend::const_iterator iKey = oKeys.begin();
2623 
2624  for (; iKey != oKeys.end(); ++iKey) {
2625  // get all values for this key
2626  TNamesDepend oValues;
2627  GetAllValues(iSection->pItem, iKey->pItem, oValues);
2628 
2629  typename TNamesDepend::const_iterator iValue = oValues.begin();
2630 
2631  for (; iValue != oValues.end(); ++iValue) {
2632  // write out the comment if there is one
2633  if (iValue->pComment) {
2634  a_oOutput.Write(SI_NEWLINE_A);
2635 
2636  if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) {
2637  return SI_FAIL;
2638  }
2639  }
2640 
2641  // write the key
2642  if (!convert.ConvertToStore(iKey->pItem)) {
2643  return SI_FAIL;
2644  }
2645 
2646  a_oOutput.Write(convert.Data());
2647 
2648  // write the value
2649  if (!convert.ConvertToStore(iValue->pItem)) {
2650  return SI_FAIL;
2651  }
2652 
2653  a_oOutput.Write(m_bSpaces ? " = " : "=");
2654 
2655  if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
2656  // multi-line data needs to be processed specially to ensure
2657  // that we use the correct newline format for the current system
2658  a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
2659 
2660  if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
2661  return SI_FAIL;
2662  }
2663 
2664  a_oOutput.Write("END_OF_TEXT");
2665  } else {
2666  a_oOutput.Write(convert.Data());
2667  }
2668 
2669  a_oOutput.Write(SI_NEWLINE_A);
2670  }
2671  }
2672 
2673  bNeedNewLine = true;
2674  }
2675 
2676  return SI_OK;
2677 }
2678 
2679 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2680 bool
2682  OutputWriter &a_oOutput,
2683  Converter &a_oConverter,
2684  const SI_CHAR *a_pText
2685 ) const {
2686  const SI_CHAR *pEndOfLine;
2687  SI_CHAR cEndOfLineChar = *a_pText;
2688 
2689  while (cEndOfLineChar) {
2690  // find the end of this line
2691  pEndOfLine = a_pText;
2692 
2693  for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;
2694 
2695  cEndOfLineChar = *pEndOfLine;
2696 
2697  // temporarily null terminate, convert and output the line
2698  *const_cast<SI_CHAR *>(pEndOfLine) = 0;
2699 
2700  if (!a_oConverter.ConvertToStore(a_pText)) {
2701  return false;
2702  }
2703 
2704  *const_cast<SI_CHAR *>(pEndOfLine) = cEndOfLineChar;
2705  a_pText += (pEndOfLine - a_pText) + 1;
2706  a_oOutput.Write(a_oConverter.Data());
2707  a_oOutput.Write(SI_NEWLINE_A);
2708  }
2709 
2710  return true;
2711 }
2712 
2713 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2714 bool
2716  const SI_CHAR *a_pSection,
2717  const SI_CHAR *a_pKey,
2718  bool a_bRemoveEmpty
2719 ) {
2720  return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty);
2721 }
2722 
2723 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2724 bool
2726  const SI_CHAR *a_pSection,
2727  const SI_CHAR *a_pKey,
2728  const SI_CHAR *a_pValue,
2729  bool a_bRemoveEmpty
2730 ) {
2731  if (!a_pSection) {
2732  return false;
2733  }
2734 
2735  typename TSection::iterator iSection = m_data.find(a_pSection);
2736 
2737  if (iSection == m_data.end()) {
2738  return false;
2739  }
2740 
2741  // remove a single key if we have a keyname
2742  if (a_pKey) {
2743  typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
2744 
2745  if (iKeyVal == iSection->second.end()) {
2746  return false;
2747  }
2748 
2749  const static SI_STRLESS isLess = SI_STRLESS();
2750 
2751  // remove any copied strings and then the key
2752  typename TKeyVal::iterator iDelete;
2753  bool bDeleted = false;
2754 
2755  do {
2756  iDelete = iKeyVal++;
2757 
2758  if (a_pValue == NULL ||
2759  (isLess(a_pValue, iDelete->second) == false &&
2760  isLess(iDelete->second, a_pValue) == false)) {
2761  DeleteString(iDelete->first.pItem);
2762  DeleteString(iDelete->second);
2763  iSection->second.erase(iDelete);
2764  bDeleted = true;
2765  }
2766  } while (iKeyVal != iSection->second.end()
2767  && !IsLess(a_pKey, iKeyVal->first.pItem));
2768 
2769  if (!bDeleted) {
2770  return false;
2771  }
2772 
2773  // done now if the section is not empty or we are not pruning away
2774  // the empty sections. Otherwise let it fall through into the section
2775  // deletion code
2776  if (!a_bRemoveEmpty || !iSection->second.empty()) {
2777  return true;
2778  }
2779  } else {
2780  // delete all copied strings from this section. The actual
2781  // entries will be removed when the section is removed.
2782  typename TKeyVal::iterator iKeyVal = iSection->second.begin();
2783 
2784  for (; iKeyVal != iSection->second.end(); ++iKeyVal) {
2785  DeleteString(iKeyVal->first.pItem);
2786  DeleteString(iKeyVal->second);
2787  }
2788  }
2789 
2790  // delete the section itself
2791  DeleteString(iSection->first.pItem);
2792  m_data.erase(iSection);
2793 
2794  return true;
2795 }
2796 
2797 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2798 void
2800  const SI_CHAR *a_pString
2801 ) {
2802  // strings may exist either inside the data block, or they will be
2803  // individually allocated and stored in m_strings. We only physically
2804  // delete those stored in m_strings.
2805  if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
2806  typename TNamesDepend::iterator i = m_strings.begin();
2807 
2808  for (; i != m_strings.end(); ++i) {
2809  if (a_pString == i->pItem) {
2810  delete[] const_cast<SI_CHAR *>(i->pItem);
2811  m_strings.erase(i);
2812  break;
2813  }
2814  }
2815  }
2816 }
2817 
2818 // ---------------------------------------------------------------------------
2819 // CONVERSION FUNCTIONS
2820 // ---------------------------------------------------------------------------
2821 
2822 // Defines the conversion classes for different libraries. Before including
2823 // SimpleIni.h, set the converter that you wish you use by defining one of the
2824 // following symbols.
2825 //
2826 // SI_CONVERT_GENERIC Use the Unicode reference conversion library in
2827 // the accompanying files ConvertUTF.h/c
2828 // SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
2829 // ICU headers on include path and icuuc.lib
2830 // SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
2831 
2832 #if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
2833 # ifdef _WIN32
2834 # define SI_CONVERT_WIN32
2835 # else
2836 # define SI_CONVERT_GENERIC
2837 # endif
2838 #endif
2839 
2845 template<class SI_CHAR>
2847  bool operator()(const SI_CHAR *pLeft, const SI_CHAR *pRight) const {
2848  long cmp;
2849 
2850  for (; *pLeft && *pRight; ++pLeft, ++pRight) {
2851  cmp = (long) * pLeft - (long) * pRight;
2852 
2853  if (cmp != 0) {
2854  return cmp < 0;
2855  }
2856  }
2857 
2858  return *pRight != 0;
2859  }
2860 };
2861 
2868 template<class SI_CHAR>
2870  inline SI_CHAR locase(SI_CHAR ch) const {
2871  return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
2872  }
2873  bool operator()(const SI_CHAR *pLeft, const SI_CHAR *pRight) const {
2874  long cmp;
2875 
2876  for (; *pLeft && *pRight; ++pLeft, ++pRight) {
2877  cmp = (long) locase(*pLeft) - (long) locase(*pRight);
2878 
2879  if (cmp != 0) {
2880  return cmp < 0;
2881  }
2882  }
2883 
2884  return *pRight != 0;
2885  }
2886 };
2887 
2891 template<class SI_CHAR>
2893  bool m_bStoreIsUtf8;
2894  protected:
2895  SI_ConvertA() { }
2896  public:
2897  SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
2898 
2899  /* copy and assignment */
2900  SI_ConvertA(const SI_ConvertA &rhs) {
2901  operator=(rhs);
2902  }
2903  SI_ConvertA &operator=(const SI_ConvertA &rhs) {
2904  m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2905  return *this;
2906  }
2907 
2922  const char *a_pInputData,
2923  size_t a_uInputDataLen) {
2924  (void)a_pInputData;
2925  SI_ASSERT(a_uInputDataLen != (size_t) -1);
2926 
2927  // ASCII/MBCS/UTF-8 needs no conversion
2928  return a_uInputDataLen;
2929  }
2930 
2945  const char *a_pInputData,
2946  size_t a_uInputDataLen,
2947  SI_CHAR *a_pOutputData,
2948  size_t a_uOutputDataSize) {
2949  // ASCII/MBCS/UTF-8 needs no conversion
2950  if (a_uInputDataLen > a_uOutputDataSize) {
2951  return false;
2952  }
2953 
2954  memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
2955  return true;
2956  }
2957 
2968  size_t SizeToStore(
2969  const SI_CHAR *a_pInputData) {
2970  // ASCII/MBCS/UTF-8 needs no conversion
2971  return strlen((const char *)a_pInputData) + 1;
2972  }
2973 
2988  const SI_CHAR *a_pInputData,
2989  char *a_pOutputData,
2990  size_t a_uOutputDataSize) {
2991  // calc input string length (SI_CHAR type and size independent)
2992  size_t uInputLen = strlen((const char *)a_pInputData) + 1;
2993 
2994  if (uInputLen > a_uOutputDataSize) {
2995  return false;
2996  }
2997 
2998  // ascii/UTF-8 needs no conversion
2999  memcpy(a_pOutputData, a_pInputData, uInputLen);
3000  return true;
3001  }
3002 };
3003 
3004 
3005 // ---------------------------------------------------------------------------
3006 // SI_CONVERT_GENERIC
3007 // ---------------------------------------------------------------------------
3008 #ifdef SI_CONVERT_GENERIC
3009 
3010 #define SI_Case SI_GenericCase
3011 #define SI_NoCase SI_GenericNoCase
3012 
3013 #include <wchar.h>
3014 #include "ConvertUTF.h"
3015 
3020 template<class SI_CHAR>
3022  bool m_bStoreIsUtf8;
3023  protected:
3024  SI_ConvertW() { }
3025  public:
3026  SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
3027 
3028  /* copy and assignment */
3029  SI_ConvertW(const SI_ConvertW &rhs) {
3030  operator=(rhs);
3031  }
3032  SI_ConvertW &operator=(const SI_ConvertW &rhs) {
3033  m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
3034  return *this;
3035  }
3036 
3051  const char *a_pInputData,
3052  size_t a_uInputDataLen) {
3053  SI_ASSERT(a_uInputDataLen != (size_t) -1);
3054 
3055  if (m_bStoreIsUtf8) {
3056  // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
3057  // so we just return the same number of characters required as for
3058  // the source text.
3059  return a_uInputDataLen;
3060  }
3061 
3062 #if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux))
3063  // fall back processing for platforms that don't support a NULL dest to mbstowcs
3064  // worst case scenario is 1:1, this will be a sufficient buffer size
3065  (void)a_pInputData;
3066  return a_uInputDataLen;
3067 #else
3068  // get the actual required buffer size
3069  return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
3070 #endif
3071  }
3072 
3087  const char *a_pInputData,
3088  size_t a_uInputDataLen,
3089  SI_CHAR *a_pOutputData,
3090  size_t a_uOutputDataSize) {
3091  if (m_bStoreIsUtf8) {
3092  // This uses the Unicode reference implementation to do the
3093  // conversion from UTF-8 to wchar_t. The required files are
3094  // ConvertUTF.h and ConvertUTF.c which should be included in
3095  // the distribution but are publically available from unicode.org
3096  // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
3097  ConversionResult retval;
3098  const UTF8 *pUtf8 = (const UTF8 *) a_pInputData;
3099 
3100  if (sizeof(wchar_t) == sizeof(UTF32)) {
3101  UTF32 *pUtf32 = (UTF32 *) a_pOutputData;
3102  retval = ConvertUTF8toUTF32(
3103  &pUtf8, pUtf8 + a_uInputDataLen,
3104  &pUtf32, pUtf32 + a_uOutputDataSize,
3105  lenientConversion);
3106  } else if (sizeof(wchar_t) == sizeof(UTF16)) {
3107  UTF16 *pUtf16 = (UTF16 *) a_pOutputData;
3108  retval = ConvertUTF8toUTF16(
3109  &pUtf8, pUtf8 + a_uInputDataLen,
3110  &pUtf16, pUtf16 + a_uOutputDataSize,
3111  lenientConversion);
3112  }
3113 
3114  return retval == conversionOK;
3115  }
3116 
3117  // convert to wchar_t
3118  size_t retval = mbstowcs(a_pOutputData,
3119  a_pInputData, a_uOutputDataSize);
3120  return retval != (size_t)(-1);
3121  }
3122 
3133  size_t SizeToStore(
3134  const SI_CHAR *a_pInputData) {
3135  if (m_bStoreIsUtf8) {
3136  // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
3137  size_t uLen = 0;
3138 
3139  while (a_pInputData[uLen]) {
3140  ++uLen;
3141  }
3142 
3143  return (6 * uLen) + 1;
3144  } else {
3145  size_t uLen = wcstombs(NULL, a_pInputData, 0);
3146 
3147  if (uLen == (size_t)(-1)) {
3148  return uLen;
3149  }
3150 
3151  return uLen + 1; // include NULL terminator
3152  }
3153  }
3154 
3169  const SI_CHAR *a_pInputData,
3170  char *a_pOutputData,
3171  size_t a_uOutputDataSize
3172  ) {
3173  if (m_bStoreIsUtf8) {
3174  // calc input string length (SI_CHAR type and size independent)
3175  size_t uInputLen = 0;
3176 
3177  while (a_pInputData[uInputLen]) {
3178  ++uInputLen;
3179  }
3180 
3181  ++uInputLen; // include the NULL char
3182 
3183  // This uses the Unicode reference implementation to do the
3184  // conversion from wchar_t to UTF-8. The required files are
3185  // ConvertUTF.h and ConvertUTF.c which should be included in
3186  // the distribution but are publically available from unicode.org
3187  // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
3188  ConversionResult retval;
3189  UTF8 *pUtf8 = (UTF8 *) a_pOutputData;
3190 
3191  if (sizeof(wchar_t) == sizeof(UTF32)) {
3192  const UTF32 *pUtf32 = (const UTF32 *) a_pInputData;
3193  retval = ConvertUTF32toUTF8(
3194  &pUtf32, pUtf32 + uInputLen,
3195  &pUtf8, pUtf8 + a_uOutputDataSize,
3196  lenientConversion);
3197  } else if (sizeof(wchar_t) == sizeof(UTF16)) {
3198  const UTF16 *pUtf16 = (const UTF16 *) a_pInputData;
3199  retval = ConvertUTF16toUTF8(
3200  &pUtf16, pUtf16 + uInputLen,
3201  &pUtf8, pUtf8 + a_uOutputDataSize,
3202  lenientConversion);
3203  }
3204 
3205  return retval == conversionOK;
3206  } else {
3207  size_t retval = wcstombs(a_pOutputData,
3208  a_pInputData, a_uOutputDataSize);
3209  return retval != (size_t) -1;
3210  }
3211  }
3212 };
3213 
3214 #endif // SI_CONVERT_GENERIC
3215 
3216 
3217 // ---------------------------------------------------------------------------
3218 // SI_CONVERT_ICU
3219 // ---------------------------------------------------------------------------
3220 #ifdef SI_CONVERT_ICU
3221 
3222 #define SI_Case SI_GenericCase
3223 #define SI_NoCase SI_GenericNoCase
3224 
3225 #include <unicode/ucnv.h>
3226 
3230 template<class SI_CHAR>
3231 class SI_ConvertW {
3232  const char *m_pEncoding;
3233  UConverter *m_pConverter;
3234  protected:
3235  SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
3236  public:
3237  SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
3238  m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
3239  }
3240 
3241  /* copy and assignment */
3242  SI_ConvertW(const SI_ConvertW &rhs) {
3243  operator=(rhs);
3244  }
3245  SI_ConvertW &operator=(const SI_ConvertW &rhs) {
3246  m_pEncoding = rhs.m_pEncoding;
3247  m_pConverter = NULL;
3248  return *this;
3249  }
3250  ~SI_ConvertW() {
3251  if (m_pConverter) {
3252  ucnv_close(m_pConverter);
3253  }
3254  }
3255 
3269  size_t SizeFromStore(
3270  const char *a_pInputData,
3271  size_t a_uInputDataLen) {
3272  SI_ASSERT(a_uInputDataLen != (size_t) -1);
3273 
3274  UErrorCode nError;
3275 
3276  if (!m_pConverter) {
3277  nError = U_ZERO_ERROR;
3278  m_pConverter = ucnv_open(m_pEncoding, &nError);
3279 
3280  if (U_FAILURE(nError)) {
3281  return (size_t) -1;
3282  }
3283  }
3284 
3285  nError = U_ZERO_ERROR;
3286  int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,
3287  a_pInputData, (int32_t) a_uInputDataLen, &nError);
3288 
3289  if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3290  return (size_t) -1;
3291  }
3292 
3293  return (size_t) nLen;
3294  }
3295 
3309  bool ConvertFromStore(
3310  const char *a_pInputData,
3311  size_t a_uInputDataLen,
3312  UChar *a_pOutputData,
3313  size_t a_uOutputDataSize) {
3314  UErrorCode nError;
3315 
3316  if (!m_pConverter) {
3317  nError = U_ZERO_ERROR;
3318  m_pConverter = ucnv_open(m_pEncoding, &nError);
3319 
3320  if (U_FAILURE(nError)) {
3321  return false;
3322  }
3323  }
3324 
3325  nError = U_ZERO_ERROR;
3326  ucnv_toUChars(m_pConverter,
3327  a_pOutputData, (int32_t) a_uOutputDataSize,
3328  a_pInputData, (int32_t) a_uInputDataLen, &nError);
3329 
3330  if (U_FAILURE(nError)) {
3331  return false;
3332  }
3333 
3334  return true;
3335  }
3336 
3347  size_t SizeToStore(
3348  const UChar *a_pInputData) {
3349  UErrorCode nError;
3350 
3351  if (!m_pConverter) {
3352  nError = U_ZERO_ERROR;
3353  m_pConverter = ucnv_open(m_pEncoding, &nError);
3354 
3355  if (U_FAILURE(nError)) {
3356  return (size_t) -1;
3357  }
3358  }
3359 
3360  nError = U_ZERO_ERROR;
3361  int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,
3362  a_pInputData, -1, &nError);
3363 
3364  if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3365  return (size_t) -1;
3366  }
3367 
3368  return (size_t) nLen + 1;
3369  }
3370 
3384  bool ConvertToStore(
3385  const UChar *a_pInputData,
3386  char *a_pOutputData,
3387  size_t a_uOutputDataSize) {
3388  UErrorCode nError;
3389 
3390  if (!m_pConverter) {
3391  nError = U_ZERO_ERROR;
3392  m_pConverter = ucnv_open(m_pEncoding, &nError);
3393 
3394  if (U_FAILURE(nError)) {
3395  return false;
3396  }
3397  }
3398 
3399  nError = U_ZERO_ERROR;
3400  ucnv_fromUChars(m_pConverter,
3401  a_pOutputData, (int32_t) a_uOutputDataSize,
3402  a_pInputData, -1, &nError);
3403 
3404  if (U_FAILURE(nError)) {
3405  return false;
3406  }
3407 
3408  return true;
3409  }
3410 };
3411 
3412 #endif // SI_CONVERT_ICU
3413 
3414 
3415 // ---------------------------------------------------------------------------
3416 // SI_CONVERT_WIN32
3417 // ---------------------------------------------------------------------------
3418 #ifdef SI_CONVERT_WIN32
3419 
3420 #define SI_Case SI_GenericCase
3421 
3422 // Windows CE doesn't have errno or MBCS libraries
3423 #ifdef _WIN32_WCE
3424 # ifndef SI_NO_MBCS
3425 # define SI_NO_MBCS
3426 # endif
3427 #endif
3428 
3429 #include <windows.h>
3430 #ifdef SI_NO_MBCS
3431 # define SI_NoCase SI_GenericNoCase
3432 #else // !SI_NO_MBCS
3433 
3441 #include <mbstring.h>
3442 template<class SI_CHAR>
3443 struct SI_NoCase {
3444  bool operator()(const SI_CHAR *pLeft, const SI_CHAR *pRight) const {
3445  if (sizeof(SI_CHAR) == sizeof(char)) {
3446  return _mbsicmp((const unsigned char *)pLeft,
3447  (const unsigned char *)pRight) < 0;
3448  }
3449 
3450  if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
3451  return _wcsicmp((const wchar_t *)pLeft,
3452  (const wchar_t *)pRight) < 0;
3453  }
3454 
3455  return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight);
3456  }
3457 };
3458 #endif // SI_NO_MBCS
3459 
3466 template<class SI_CHAR>
3467 class SI_ConvertW {
3468  UINT m_uCodePage;
3469  protected:
3470  SI_ConvertW() { }
3471  public:
3472  SI_ConvertW(bool a_bStoreIsUtf8) {
3473  m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP;
3474  }
3475 
3476  /* copy and assignment */
3477  SI_ConvertW(const SI_ConvertW &rhs) {
3478  operator=(rhs);
3479  }
3480  SI_ConvertW &operator=(const SI_ConvertW &rhs) {
3481  m_uCodePage = rhs.m_uCodePage;
3482  return *this;
3483  }
3484 
3498  size_t SizeFromStore(
3499  const char *a_pInputData,
3500  size_t a_uInputDataLen) {
3501  SI_ASSERT(a_uInputDataLen != (size_t) -1);
3502 
3503  int retval = MultiByteToWideChar(
3504  m_uCodePage, 0,
3505  a_pInputData, (int) a_uInputDataLen,
3506  0, 0);
3507  return (size_t)(retval > 0 ? retval : -1);
3508  }
3509 
3523  bool ConvertFromStore(
3524  const char *a_pInputData,
3525  size_t a_uInputDataLen,
3526  SI_CHAR *a_pOutputData,
3527  size_t a_uOutputDataSize) {
3528  int nSize = MultiByteToWideChar(
3529  m_uCodePage, 0,
3530  a_pInputData, (int) a_uInputDataLen,
3531  (wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
3532  return (nSize > 0);
3533  }
3534 
3545  size_t SizeToStore(
3546  const SI_CHAR *a_pInputData) {
3547  int retval = WideCharToMultiByte(
3548  m_uCodePage, 0,
3549  (const wchar_t *) a_pInputData, -1,
3550  0, 0, 0, 0);
3551  return (size_t)(retval > 0 ? retval : -1);
3552  }
3553 
3567  bool ConvertToStore(
3568  const SI_CHAR *a_pInputData,
3569  char *a_pOutputData,
3570  size_t a_uOutputDataSize) {
3571  int retval = WideCharToMultiByte(
3572  m_uCodePage, 0,
3573  (const wchar_t *) a_pInputData, -1,
3574  a_pOutputData, (int) a_uOutputDataSize, 0, 0);
3575  return retval > 0;
3576  }
3577 };
3578 
3579 #endif // SI_CONVERT_WIN32
3580 
3581 
3582 // ---------------------------------------------------------------------------
3583 // TYPE DEFINITIONS
3584 // ---------------------------------------------------------------------------
3585 
3586 typedef CSimpleIniTempl<char,
3587  SI_NoCase<char>, SI_ConvertA<char> > CSimpleIniA;
3588 typedef CSimpleIniTempl<char,
3589  SI_Case<char>, SI_ConvertA<char> > CSimpleIniCaseA;
3590 
3591 #if defined(SI_CONVERT_ICU)
3592 typedef CSimpleIniTempl<UChar,
3593  SI_NoCase<UChar>, SI_ConvertW<UChar> > CSimpleIniW;
3594 typedef CSimpleIniTempl<UChar,
3595  SI_Case<UChar>, SI_ConvertW<UChar> > CSimpleIniCaseW;
3596 #else
3597 typedef CSimpleIniTempl<wchar_t,
3598  SI_NoCase<wchar_t>, SI_ConvertW<wchar_t> > CSimpleIniW;
3599 typedef CSimpleIniTempl<wchar_t,
3600  SI_Case<wchar_t>, SI_ConvertW<wchar_t> > CSimpleIniCaseW;
3601 #endif
3602 
3603 #ifdef _UNICODE
3604 # define CSimpleIni CSimpleIniW
3605 # define CSimpleIniCase CSimpleIniCaseW
3606 # define SI_NEWLINE SI_NEWLINE_W
3607 #else // !_UNICODE
3608 # define CSimpleIni CSimpleIniA
3609 # define CSimpleIniCase CSimpleIniCaseA
3610 # define SI_NEWLINE SI_NEWLINE_A
3611 #endif // _UNICODE
3612 
3613 #ifdef _MSC_VER
3614 # pragma warning (pop)
3615 #endif
3616 
3617 #endif // INCLUDED_SimpleIni_h
3618 
Definition: SimpleIni.h:2892
void SetSpaces(bool a_bSpaces=true)
Definition: SimpleIni.h:561
bool GetAllKeys(const SI_CHAR *a_pSection, TNamesDepend &a_names) const
Definition: SimpleIni.h:2447
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:986
Definition: SimpleIni.h:2846
Definition: SimpleIni.h:344
void SetMultiLine(bool a_bAllowMultiLine=true)
Definition: SimpleIni.h:546
SI_Error LoadFile(const char *a_pszFile)
Definition: SimpleIni.h:1349
bool GetAllValues(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, TNamesDepend &a_values) const
Definition: SimpleIni.h:2340
double GetDoubleValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, double a_nDefault=0, bool *a_pHasMultiple=NULL) const
Definition: SimpleIni.h:2199
Definition: SimpleIni.h:295
Definition: SimpleIni.h:423
CSimpleIniTempl(bool a_bIsUtf8=false, bool a_bMultiKey=false, bool a_bMultiLine=false)
Definition: SimpleIni.h:1302
SI_Error Save(OutputWriter &a_oOutput, bool a_bAddSignature=false) const
Definition: SimpleIni.h:2541
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:2079
bool DeleteValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pValue, bool a_bRemoveEmpty=false)
Definition: SimpleIni.h:2725
void GetAllSections(TNamesDepend &a_names) const
Definition: SimpleIni.h:2434
bool GetBoolValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bDefault=false, bool *a_pHasMultiple=NULL) const
Definition: SimpleIni.h:2265
Definition: SimpleIni.h:3021
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:2233
Definition: SimpleIni.h:380
size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen)
Definition: SimpleIni.h:2921
bool Delete(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bRemoveEmpty=false)
Definition: SimpleIni.h:2715
bool IsMultiKey() const
Definition: SimpleIni.h:535
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:2166
Converter GetConverter() const
Definition: SimpleIni.h:1143
size_t SizeToStore(const SI_CHAR *a_pInputData)
Definition: SimpleIni.h:2968
Definition: SimpleIni.h:393
Definition: SimpleIni.h:300
bool UsingSpaces() const
Definition: SimpleIni.h:566
bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData, size_t a_uOutputDataSize)
Definition: SimpleIni.h:3168
std::map< Entry, TKeyVal, typename Entry::KeyOrder > TSection
Definition: SimpleIni.h:359
void Reset()
Definition: SimpleIni.h:1324
SI_Error SaveFile(const char *a_pszFile, bool a_bAddSignature=true) const
Definition: SimpleIni.h:2479
Definition: SimpleIni.h:336
SI_Error LoadData(const std::string &a_strData)
Definition: SimpleIni.h:626
const TKeyVal * GetSection(const SI_CHAR *a_pSection) const
Definition: SimpleIni.h:2418
void SetUnicode(bool a_bIsUtf8=true)
Definition: SimpleIni.h:501
void SetMultiKey(bool a_bAllowMultiKey=true)
Definition: SimpleIni.h:530
bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen, SI_CHAR *a_pOutputData, size_t a_uOutputDataSize)
Definition: SimpleIni.h:2944
bool IsMultiLine() const
Definition: SimpleIni.h:551
int GetSectionSize(const SI_CHAR *a_pSection) const
Definition: SimpleIni.h:2380
bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen, SI_CHAR *a_pOutputData, size_t a_uOutputDataSize)
Definition: SimpleIni.h:3086
bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData, size_t a_uOutputDataSize)
Definition: SimpleIni.h:2987
bool IsEmpty() const
Definition: SimpleIni.h:480
bool IsUnicode() const
Definition: SimpleIni.h:508
size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen)
Definition: SimpleIni.h:3050
Definition: SimpleIni.h:369
std::list< Entry > TNamesDepend
Definition: SimpleIni.h:364
~CSimpleIniTempl()
Definition: SimpleIni.h:1318
Definition: SimpleIni.h:2869
std::multimap< Entry, const SI_CHAR *, typename Entry::KeyOrder > TKeyVal
Definition: SimpleIni.h:356
long GetLongValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, long a_nDefault=0, bool *a_pHasMultiple=NULL) const
Definition: SimpleIni.h:2121
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:2313
size_t SizeToStore(const SI_CHAR *a_pInputData)
Definition: SimpleIni.h:3133