22 #pragma warning(disable : 4127 4503 4702 4786) 36 #define SI_SUPPORT_IOSTREAMS 38 #ifdef SI_SUPPORT_IOSTREAMS 40 #endif // SI_SUPPORT_IOSTREAMS 44 #define SI_ASSERT(x) assert(x) 70 #define SI_NEWLINE_A "\r\n" 71 #define SI_NEWLINE_W L"\r\n" 73 #define SI_NEWLINE_A "\n" 74 #define SI_NEWLINE_W L"\n" 78 #define SI_HAS_WIDE_FILE 79 #define SI_WCHAR_T wchar_t 80 #elif defined(SI_CONVERT_ICU) 81 #define SI_HAS_WIDE_FILE 82 #define SI_WCHAR_T UChar 108 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
119 Entry(
const SI_CHAR* a_pszItem =
nullptr,
int a_nOrder = 0)
132 #if (defined(_MSC_VER) && _MSC_VER <= 1200) 137 return LoadOrder()(*
this, rhs);
139 bool operator>(
const Entry& rhs)
const 141 return LoadOrder()(rhs, *
this);
146 struct KeyOrder : std::function<bool(Entry, Entry)>
150 const static SI_STRLESS isLess = SI_STRLESS();
171 std::multimap<Entry, const SI_CHAR*, typename Entry::KeyOrder>;
174 using TSection = std::map<Entry, TKeyVal, typename Entry::KeyOrder>;
189 virtual void Write(
const char* a_pBuf) = 0;
203 void Write(
const char* a_pBuf)
override { fputs(a_pBuf,
m_file); }
224 #ifdef SI_SUPPORT_IOSTREAMS 238 #endif // SI_SUPPORT_IOSTREAMS 255 size_t uLen = this->SizeToStore(a_pszString);
256 if (uLen == (
size_t)(-1))
264 return SI_CONVERTER::ConvertToStore(
265 a_pszString, const_cast<char*>(
m_scratch.data()),
363 #ifdef SI_HAS_WIDE_FILE 371 #endif // SI_HAS_WIDE_FILE 382 #ifdef SI_SUPPORT_IOSTREAMS 390 #endif // SI_SUPPORT_IOSTREAMS 400 return Load(a_strData.c_str(), a_strData.size());
426 #ifdef SI_HAS_WIDE_FILE 473 #ifdef SI_SUPPORT_IOSTREAMS 485 #endif // SI_SUPPORT_IOSTREAMS 549 const SI_CHAR* a_pSection,
const SI_CHAR* a_pKey,
597 const SI_CHAR* a_pSection,
const SI_CHAR* a_pKey,
598 const SI_CHAR* a_pDefault =
nullptr,
599 bool* a_pHasMultiple =
nullptr)
const;
625 const SI_CHAR* a_pSection,
const SI_CHAR* a_pKey,
626 const SI_CHAR* a_pValue,
const SI_CHAR* a_pComment =
nullptr)
628 return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment,
true);
650 const SI_CHAR* a_pSection,
const SI_CHAR* a_pKey,
651 bool a_bRemoveEmpty =
false);
676 SI_CHAR*& a_pData,
const SI_CHAR*& a_pSection,
const SI_CHAR*& a_pKey,
677 const SI_CHAR*& a_pVal,
const SI_CHAR*& a_pComment)
const;
696 const SI_CHAR* a_pSection,
const SI_CHAR* a_pKey,
697 const SI_CHAR* a_pValue,
const SI_CHAR* a_pComment,
698 bool a_bCopyStrings);
701 inline bool IsSpace(SI_CHAR ch)
const 703 return (ch ==
' ' || ch ==
'\t' || ch ==
'\r' || ch ==
'\n');
707 inline bool IsComment(SI_CHAR ch)
const {
return (ch ==
';' || ch ==
'#'); }
711 a_pData += (*a_pData ==
'\r' && *(a_pData + 1) ==
'\n') ? 2 : 1;
721 bool IsLess(
const SI_CHAR* a_pLeft,
const SI_CHAR* a_pRight)
const 723 const static SI_STRLESS isLess = SI_STRLESS();
724 return isLess(a_pLeft, a_pRight);
730 SI_CHAR*& a_pData,
const SI_CHAR*& a_pVal,
const SI_CHAR* a_pTagName,
731 bool a_bAllowBlankLinesInComment =
false)
const;
735 OutputWriter& a_oOutput, Converter& a_oConverter,
736 const SI_CHAR* a_pText)
const;
780 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
782 bool a_bAllowMultiKey,
bool a_bAllowMultiLine)
785 m_pFileComment(NULL),
786 m_bAllowMultiKey(a_bAllowMultiKey),
787 m_bAllowMultiLine(a_bAllowMultiLine),
792 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
798 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
805 m_pFileComment =
nullptr;
808 m_data.erase(m_data.begin(), m_data.end());
812 if (!m_strings.empty())
814 auto i = m_strings.begin();
815 for (; i != m_strings.end(); ++i)
817 delete[]
const_cast<SI_CHAR*
>(i->pItem);
819 m_strings.erase(m_strings.begin(), m_strings.end());
823 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
825 const char* a_pszFile)
828 #if __STDC_WANT_SECURE_LIB__ 829 fopen_s(&fp, a_pszFile,
"rb");
831 fp =
fopen(a_pszFile,
"rb");
842 #ifdef SI_HAS_WIDE_FILE 843 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
849 #if __STDC_WANT_SECURE_LIB__ 850 _wfopen_s(&fp, a_pwszFile, L
"rb");
852 fp = _wfopen(a_pwszFile, L
"rb");
858 #else // SI_CONVERT_ICU 860 u_austrncpy(szFile, a_pwszFile,
sizeof(szFile));
861 return LoadFile(szFile);
864 #endif // SI_HAS_WIDE_FILE 866 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
871 int retval = fseek(a_fpFile, 0,
SEEK_END);
876 long lSize = ftell(a_fpFile);
881 char* pData =
new char[lSize];
887 size_t uRead = fread(pData,
sizeof(
char), lSize, a_fpFile);
888 if (uRead != (
size_t)lSize)
900 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
902 const char* a_pData,
size_t a_uDataLen)
904 SI_CONVERTER converter;
907 size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
908 if (uLen == (
size_t)(-1))
915 auto* pData =
new SI_CHAR[uLen + 1];
920 memset(pData, 0,
sizeof(SI_CHAR) * (uLen + 1));
923 if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen))
930 const static SI_CHAR
empty = 0;
931 SI_CHAR* pWork = pData;
932 const SI_CHAR* pSection = &
empty;
933 const SI_CHAR* pItem =
nullptr;
934 const SI_CHAR* pVal =
nullptr;
935 const SI_CHAR* pComment =
nullptr;
939 bool bCopyStrings = (m_pData !=
nullptr);
943 SI_Error rc = FindFileComment(pWork, bCopyStrings);
944 if (rc < 0)
return rc;
947 while (FindEntry(pWork, pSection, pItem, pVal, pComment))
949 rc = AddEntry(pSection, pItem, pVal, pComment, bCopyStrings);
950 if (rc < 0)
return rc;
961 m_uDataLen = uLen + 1;
967 #ifdef SI_SUPPORT_IOSTREAMS 968 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
970 std::istream& a_istream)
976 a_istream.get(szBuf,
sizeof(szBuf),
'\0');
977 strData.append(szBuf);
978 }
while (a_istream.good());
979 return Load(strData);
981 #endif // SI_SUPPORT_IOSTREAMS 983 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
985 SI_CHAR*& a_pData,
bool a_bCopyStrings)
995 if (!LoadMultiLineText(a_pData, m_pFileComment,
nullptr,
false))
1003 SI_Error rc = CopyString(m_pFileComment);
1004 if (rc < 0)
return rc;
1010 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1012 SI_CHAR*& a_pData,
const SI_CHAR*& a_pSection,
const SI_CHAR*& a_pKey,
1013 const SI_CHAR*& a_pVal,
const SI_CHAR*& a_pComment)
const 1015 a_pComment =
nullptr;
1017 SI_CHAR* pTrail =
nullptr;
1021 while (*a_pData && IsSpace(*a_pData))
1032 if (IsComment(*a_pData))
1034 LoadMultiLineText(a_pData, a_pComment,
nullptr,
true);
1039 if (*a_pData ==
'[')
1043 while (*a_pData && IsSpace(*a_pData))
1050 a_pSection = a_pData;
1051 while (*a_pData && *a_pData !=
']' && !IsNewLineChar(*a_pData))
1057 if (*a_pData !=
']')
1063 pTrail = a_pData - 1;
1064 while (pTrail >= a_pSection && IsSpace(*pTrail))
1073 while (*a_pData && !IsNewLineChar(*a_pData))
1086 while (*a_pData && *a_pData !=
'=' && !IsNewLineChar(*a_pData))
1092 if (*a_pData !=
'=')
1098 if (a_pKey == a_pData)
1100 while (*a_pData && !IsNewLineChar(*a_pData))
1108 pTrail = a_pData - 1;
1109 while (pTrail >= a_pKey && IsSpace(*pTrail))
1118 while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData))
1125 while (*a_pData && !IsNewLineChar(*a_pData))
1131 pTrail = a_pData - 1;
1134 SkipNewLine(a_pData);
1136 while (pTrail >= a_pVal && IsSpace(*pTrail))
1144 if (m_bAllowMultiLine && IsMultiLineTag(a_pVal))
1147 const SI_CHAR* pTagName = a_pVal + 3;
1148 return LoadMultiLineText(a_pData, a_pVal, pTagName);
1158 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1160 const SI_CHAR* a_pVal)
const 1163 if (*a_pVal++ !=
'<')
return false;
1164 if (*a_pVal++ !=
'<')
return false;
1165 if (*a_pVal++ !=
'<')
return false;
1169 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1171 const SI_CHAR* a_pData)
const 1185 if (IsSpace(*a_pData))
1193 if (IsNewLineChar(*a_pData))
1201 if (IsSpace(*--a_pData))
1209 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1213 return (a_c ==
'\n' || a_c ==
'\r');
1216 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1218 SI_CHAR*& a_pData,
const SI_CHAR*& a_pVal,
const SI_CHAR* a_pTagName,
1219 bool a_bAllowBlankLinesInComment)
const 1229 SI_CHAR* pDataLine = a_pData;
1238 SI_CHAR cEndOfLineChar = *a_pData;
1243 if (!a_pTagName && !IsComment(*a_pData))
1246 if (!a_bAllowBlankLinesInComment)
1254 SI_CHAR* pCurr = a_pData;
1256 while (IsSpace(*pCurr))
1258 if (IsNewLineChar(*pCurr))
1271 if (IsComment(*pCurr))
1273 for (; nNewLines > 0; --nNewLines) *pDataLine++ =
'\n';
1283 pCurrLine = a_pData;
1284 while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
1287 if (pDataLine < pCurrLine)
1289 memmove(pDataLine, pCurrLine, a_pData - pCurrLine);
1290 pDataLine[a_pData - pCurrLine] =
'\0';
1294 cEndOfLineChar = *a_pData;
1301 (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)))
1308 if (!cEndOfLineChar)
1315 pDataLine += (a_pData - pCurrLine);
1316 *a_pData = cEndOfLineChar;
1317 SkipNewLine(a_pData);
1318 *pDataLine++ =
'\n';
1322 if (a_pVal == a_pData)
1332 *--pDataLine =
'\0';
1336 if (a_pTagName && cEndOfLineChar)
1338 SI_ASSERT(IsNewLineChar(cEndOfLineChar));
1339 *a_pData = cEndOfLineChar;
1340 SkipNewLine(a_pData);
1346 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1348 const SI_CHAR*& a_pString)
1351 if (
sizeof(SI_CHAR) ==
sizeof(
char))
1353 uLen = strlen((
const char*)a_pString);
1355 else if (
sizeof(SI_CHAR) ==
sizeof(
wchar_t))
1357 uLen = wcslen((
const wchar_t*)a_pString);
1361 for (; a_pString[uLen]; ++uLen)
1365 auto* pCopy =
new SI_CHAR[uLen];
1370 memcpy(pCopy, a_pString,
sizeof(SI_CHAR) * uLen);
1371 m_strings.push_back(pCopy);
1376 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1378 const SI_CHAR* a_pSection,
const SI_CHAR* a_pKey,
const SI_CHAR* a_pValue,
1379 const SI_CHAR* a_pComment,
bool a_bCopyStrings)
1382 bool bInserted =
false;
1384 SI_ASSERT(!a_pComment || IsComment(*a_pComment));
1388 if (a_bCopyStrings && a_pComment)
1390 rc = CopyString(a_pComment);
1391 if (rc < 0)
return rc;
1395 auto iSection = m_data.end();
1398 iSection = m_data.find(a_pSection);
1399 if (iSection == m_data.end())
1404 rc = CopyString(a_pSection);
1405 if (rc < 0)
return rc;
1410 if (iSection == m_data.end())
1412 Entry oKey(a_pSection, ++m_nOrder);
1413 if (a_pComment && (!a_pKey || !a_pValue))
1417 typename TSection::value_type oEntry(oKey,
TKeyVal());
1418 using SectionIterator =
typename TSection::iterator;
1419 std::pair<SectionIterator, bool> i = m_data.insert(oEntry);
1423 if (!a_pKey || !a_pValue)
1430 TKeyVal& keyval = iSection->second;
1431 auto iKey = keyval.find(a_pKey);
1436 if (m_bAllowMultiKey || iKey == keyval.end())
1441 rc = CopyString(a_pKey);
1442 if (rc < 0)
return rc;
1446 rc = CopyString(a_pValue);
1447 if (rc < 0)
return rc;
1451 if (iKey == keyval.end() || m_bAllowMultiKey)
1453 Entry oKey(a_pKey, ++m_nOrder);
1458 typename TKeyVal::value_type oEntry(
1459 oKey, static_cast<const char*>(
nullptr));
1460 iKey = keyval.insert(oEntry);
1463 iKey->second = a_pValue;
1467 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1469 const SI_CHAR* a_pSection,
const SI_CHAR* a_pKey,
const SI_CHAR* a_pDefault,
1470 bool* a_pHasMultiple)
const 1474 *a_pHasMultiple =
false;
1476 if (!a_pSection || !a_pKey)
1480 auto iSection = m_data.find(a_pSection);
1481 if (iSection == m_data.end())
1485 auto iKeyVal = iSection->second.find(a_pKey);
1486 if (iKeyVal == iSection->second.end())
1492 if (m_bAllowMultiKey && a_pHasMultiple)
1494 auto iTemp = iKeyVal;
1495 if (++iTemp != iSection->second.end())
1497 if (!IsLess(a_pKey, iTemp->first.pItem))
1499 *a_pHasMultiple =
true;
1504 return iKeyVal->second;
1507 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1509 const SI_CHAR* a_pSection,
const SI_CHAR* a_pKey,
1512 if (!a_pSection || !a_pKey)
1516 auto iSection = m_data.find(a_pSection);
1517 if (iSection == m_data.end())
1521 auto iKeyVal = iSection->second.find(a_pKey);
1522 if (iKeyVal == iSection->second.end())
1528 a_values.push_back(iKeyVal->second);
1529 if (m_bAllowMultiKey)
1532 while (iKeyVal != iSection->second.end() &&
1533 !IsLess(a_pKey, iKeyVal->first.pItem))
1535 a_values.push_back(
Entry(iKeyVal->second, iKeyVal->first.nOrder));
1543 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1545 const SI_CHAR* a_pSection)
const 1552 typename TSection::const_iterator iSection = m_data.find(a_pSection);
1553 if (iSection == m_data.end())
1557 const TKeyVal& section = iSection->second;
1561 if (!m_bAllowMultiKey || section.empty())
1563 return (
int)section.size();
1568 const SI_CHAR* pLastKey =
nullptr;
1569 typename TKeyVal::const_iterator iKeyVal = section.begin();
1570 for (
int n = 0; iKeyVal != section.end(); ++iKeyVal, ++
n)
1572 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem))
1575 pLastKey = iKeyVal->first.pItem;
1581 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1584 const SI_CHAR* a_pSection)
const 1588 typename TSection::const_iterator i = m_data.find(a_pSection);
1589 if (i != m_data.end())
1591 return &(i->second);
1597 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1601 auto i = m_data.begin();
1602 for (
int n = 0; i != m_data.end(); ++i, ++
n)
1604 a_names.push_back(i->first);
1608 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1610 const SI_CHAR* a_pSection,
TNamesDepend& a_names)
const 1617 auto iSection = m_data.find(a_pSection);
1618 if (iSection == m_data.end())
1623 const TKeyVal& section = iSection->second;
1624 const SI_CHAR* pLastKey =
nullptr;
1625 auto iKeyVal = section.begin();
1626 for (
int n = 0; iKeyVal != section.end(); ++iKeyVal, ++
n)
1628 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem))
1630 a_names.push_back(iKeyVal->first);
1631 pLastKey = iKeyVal->first.pItem;
1638 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1640 const char* a_pszFile)
const 1643 #if __STDC_WANT_SECURE_LIB__ 1644 fopen_s(&fp, a_pszFile,
"wb");
1646 fp =
fopen(a_pszFile,
"wb");
1654 #ifdef SI_HAS_WIDE_FILE 1655 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1660 FILE* fp = _wfopen(a_pwszFile, L
"wb");
1665 #else // SI_CONVERT_ICU 1667 u_austrncpy(szFile, a_pwszFile,
sizeof(szFile));
1668 return SaveFile(szFile);
1671 #endif // SI_HAS_WIDE_FILE 1673 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1675 FILE* a_pFile)
const 1678 return Save(writer);
1681 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1689 GetAllSections(oSections);
1690 #if (defined(_MSC_VER) && _MSC_VER <= 1200) 1697 bool bNeedNewLine =
false;
1700 if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment))
1704 bNeedNewLine =
true;
1708 auto iSection = oSections.begin();
1709 for (; iSection != oSections.end(); ++iSection)
1712 if (iSection->pComment)
1725 bNeedNewLine =
false;
1732 bNeedNewLine =
false;
1736 if (*iSection->pItem)
1742 a_oOutput.
Write(
"[");
1744 a_oOutput.
Write(
"]");
1750 GetAllKeys(iSection->pItem, oKeys);
1751 #if (defined(_MSC_VER) && _MSC_VER <= 1200) 1758 auto iKey = oKeys.begin();
1759 for (; iKey != oKeys.end(); ++iKey)
1763 GetAllValues(iSection->pItem, iKey->pItem, oValues);
1769 if (!OutputMultiLineText(a_oOutput, convert, iKey->pComment))
1775 auto iValue = oValues.begin();
1776 for (; iValue != oValues.end(); ++iValue)
1790 a_oOutput.
Write(
"=");
1791 if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem))
1797 if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem))
1801 a_oOutput.
Write(
"SI-END-OF-MULTILINE-TEXT");
1811 bNeedNewLine =
true;
1817 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1820 const SI_CHAR* a_pText)
const 1822 const SI_CHAR* pEndOfLine;
1823 SI_CHAR cEndOfLineChar = *a_pText;
1824 while (cEndOfLineChar)
1827 pEndOfLine = a_pText;
1828 for (; *pEndOfLine && *pEndOfLine !=
'\n'; ++pEndOfLine)
1830 cEndOfLineChar = *pEndOfLine;
1833 *
const_cast<SI_CHAR*
>(pEndOfLine) = 0;
1838 *
const_cast<SI_CHAR*
>(pEndOfLine) = cEndOfLineChar;
1839 a_pText += (pEndOfLine - a_pText) + 1;
1846 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1848 const SI_CHAR* a_pSection,
const SI_CHAR* a_pKey,
bool a_bRemoveEmpty)
1855 typename TSection::iterator iSection = m_data.find(a_pSection);
1856 if (iSection == m_data.end())
1864 typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
1865 if (iKeyVal == iSection->second.end())
1871 typename TKeyVal::iterator iDelete;
1874 iDelete = iKeyVal++;
1876 DeleteString(iDelete->first.pItem);
1877 DeleteString(iDelete->second);
1878 iSection->second.erase(iDelete);
1879 }
while (iKeyVal != iSection->second.end() &&
1880 !IsLess(a_pKey, iKeyVal->first.pItem));
1885 if (!a_bRemoveEmpty || !iSection->second.empty())
1894 typename TKeyVal::iterator iKeyVal = iSection->second.begin();
1895 for (; iKeyVal != iSection->second.end(); ++iKeyVal)
1897 DeleteString(iKeyVal->first.pItem);
1898 DeleteString(iKeyVal->second);
1903 DeleteString(iSection->first.pItem);
1904 m_data.erase(iSection);
1909 template <
class SI_CHAR,
class SI_STRLESS,
class SI_CONVERTER>
1911 const SI_CHAR* a_pString)
1916 if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen)
1918 typename TNamesDepend::iterator i = m_strings.begin();
1919 for (; i != m_strings.end(); ++i)
1921 if (a_pString == i->pItem)
1923 delete[]
const_cast<SI_CHAR*
>(i->pItem);
1940 template <
class SI_CHAR>
1943 bool operator()(
const SI_CHAR* pLeft,
const SI_CHAR* pRight)
const 1946 for (; *pLeft && *pRight; ++pLeft, ++pRight)
1948 cmp = (long)*pLeft - (
long)*pRight;
1954 return *pRight != 0;
1964 template <
class SI_CHAR>
1969 return (ch < 'A' || ch >
'Z') ? ch : (ch -
'A' +
'a');
1971 bool operator()(
const SI_CHAR* pLeft,
const SI_CHAR* pRight)
const 1974 for (; *pLeft && *pRight; ++pLeft, ++pRight)
1982 return *pRight != 0;
1989 template <
class SI_CHAR>
2013 const char* a_pInputData,
size_t a_uInputDataLen)
2016 SI_ASSERT(a_uInputDataLen != (
size_t)-1);
2019 return a_uInputDataLen;
2037 const char* a_pInputData,
size_t a_uInputDataLen,
2038 SI_CHAR* a_pOutputData,
size_t a_uOutputDataSize)
2041 if (a_uInputDataLen > a_uOutputDataSize)
2045 memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
2063 return strlen((
const char*)a_pInputData) + 1;
2080 const SI_CHAR* a_pInputData,
char* a_pOutputData,
2081 size_t a_uOutputDataSize)
2084 size_t uInputLen = strlen((
const char*)a_pInputData) + 1;
2085 if (uInputLen > a_uOutputDataSize)
2091 memcpy(a_pOutputData, a_pInputData, uInputLen);
2114 const char* a_pInputData,
size_t a_uInputDataLen)
override 2116 SI_ASSERT(a_uInputDataLen != (
size_t)-1);
2117 return do_parse(a_pInputData, a_uInputDataLen,
nullptr);
2121 const char* a_pInputData,
size_t a_uInputDataLen,
char* a_pOutputData,
2122 size_t a_uOutputDataSize)
override 2124 this->
do_parse(a_pInputData, a_uInputDataLen, a_pOutputData);
2140 while (expr.size() > 5)
2142 auto p = expr.find(
"$env{");
2143 if (
p != std::string::npos)
2145 auto pend = expr.find(
"}",
p);
2146 if (pend == std::string::npos)
2148 "Line %u: Expected closing `}` near: `%s`",
2150 const auto substr = expr.substr(
p + 5, pend -
p - 5);
2152 auto env_val = ::getenv(substr.c_str());
2154 new_expr += expr.substr(pend + 1);
2155 new_expr.swap(expr);
2157 else if ((
p = expr.find(
"$eval{")) != std::string::npos)
2159 auto pend = expr.find(
"}",
p);
2160 if (pend == std::string::npos)
2162 "Line %u: Expected closing `}` near: `%s`",
2165 const auto substr = expr.substr(
p + 6, pend -
p - 6);
2173 new_expr += expr.substr(pend + 1);
2174 new_expr.swap(expr);
2186 if (!var_name.empty())
2189 if (!var_value.empty())
2199 size_t do_parse(
const char* in_str,
const size_t in_len,
char* out_str)
2202 size_t out_len = 0, i = 0;
2205 const char c = in_str[i];
2211 if (
c ==
'\\' && i < in_len - 1 &&
2212 (in_str[i + 1] ==
'\r' || in_str[i + 1] ==
'\n'))
2216 if (i < in_len - 2 && in_str[i + 1] ==
'\r' &&
2217 in_str[i + 2] ==
'\n')
2222 else if (in_str[i + 1] ==
'\r' || in_str[i + 1] ==
'\n')
2229 throw std::runtime_error(
2230 "MRPT_IniFileParser: parse error, shouldn't reach " 2237 if (in_len > i + 7 && !::strncmp(in_str + i,
"@define", 7))
2242 bool in_var_name =
false, done_var_name =
false;
2243 while (i < in_len && in_str[i] !=
'\r' && in_str[i] !=
'\n')
2245 const char ch = in_str[i];
2247 if (ch !=
' ' && ch !=
'\t')
2250 if (!in_var_name && !done_var_name)
2260 in_var_name =
false;
2261 done_var_name =
true;
2279 if (in_len > i + 4 && in_str[i] ==
'$' && in_str[i + 1] ==
'{')
2284 bool end_ok =
false;
2285 while (i < in_len && in_str[i] !=
'\n' && in_str[i] !=
'\r')
2287 const char ch = in_str[i];
2299 "Line %u: Expected closing `}` near: `%s`",
2306 "Line %u: Unknown variable `${%s}`", pc.
line_count,
2311 for (
const char ch : str_out)
2313 if (out_str) out_str[out_len] = ch;
2320 if (in_len > i + 7 && !strncmp(in_str + i,
"$eval{", 6))
2324 bool end_ok =
false;
2325 while (i < in_len && in_str[i] !=
'\n' && in_str[i] !=
'\r')
2327 const char ch = in_str[i];
2339 "Line %u: Expected closing `}` near: `%s`",
2345 for (
const char ch :
res)
2347 if (out_str) out_str[out_len] = ch;
2354 if (in_len > i + 6 && !strncmp(in_str + i,
"$env{", 5))
2358 bool end_ok =
false;
2359 while (i < in_len && in_str[i] !=
'\n' && in_str[i] !=
'\r')
2361 const char ch = in_str[i];
2373 "Line %u: Expected closing `}` near: `%s`",
2379 for (
const char ch :
res)
2381 if (out_str) out_str[out_len] = ch;
2390 out_str[out_len] =
c;
2413 #define CSimpleIni CSimpleIniW 2414 #define CSimpleIniCase CSimpleIniCaseW 2415 #define SI_NEWLINE SI_NEWLINE_W 2417 #define CSimpleIni CSimpleIniA 2418 #define CSimpleIniCase CSimpleIniCaseA 2419 #define SI_NEWLINE SI_NEWLINE_A SI_Error LoadFile(const char *a_pszFile)
Load an INI file from disk into memory.
bool IsComment(SI_CHAR ch) const
Does the supplied character start a comment line?
void GetAllSections(TNamesDepend &a_names) const
Retrieve all section names.
SI_CHAR locase(SI_CHAR ch) const
StringWriter(std::string &a_string)
Entry(const SI_CHAR *a_pszItem=nullptr, int a_nOrder=0)
virtual bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen, SI_CHAR *a_pOutputData, size_t a_uOutputDataSize)
Convert the input string from the storage format to SI_CHAR.
bool m_bAllowMultiLine
Are data values permitted to span multiple lines?
SI_Error CopyString(const SI_CHAR *&a_pString)
Make a copy of the supplied string, replacing the original pointer.
Strict less ordering by order, and then name of key.
bool IsMultiLineData(const SI_CHAR *a_pData) const
StreamWriter(std::ostream &a_ostream)
TSection m_data
Parsed INI data.
int void fclose(FILE *f)
An OS-independent version of fclose.
Generic ASCII case-insensitive less than comparison.
OutputWriter class to write the INI data to a string.
void DeleteString(const SI_CHAR *a_pString)
Delete a string from the copied strings buffer if necessary.
A wrapper of exprtk runtime expression compiler: it takes a string representing an expression (from a...
Strict less ordering by name of key only.
bool ConvertToStore(const SI_CHAR *a_pszString)
bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen, char *a_pOutputData, size_t a_uOutputDataSize) override
Convert the input string from the storage format to SI_CHAR.
void Write(const char *a_pBuf) override
bool IsMultiLineTag(const SI_CHAR *a_pData) const
StringWriter & operator=(const StringWriter &)
bool IsMultiKey() const
Get the storage format of the INI data.
bool Delete(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bRemoveEmpty=false)
Delete an entire section, or a key from a section.
A new value was inserted.
FileWriter & operator=(const FileWriter &)
const SI_CHAR * GetValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pDefault=nullptr, bool *a_pHasMultiple=nullptr) const
Retrieve the value for a specific key.
~CSimpleIniTempl()
Destructor.
virtual void Write(const char *a_pBuf)=0
void Reset()
Deallocate all memory stored by this object.
OutputWriter class to write the INI data to a file.
Characterset conversion utility class to convert strings to the same format as is used for the storag...
bool operator()(const Entry &lhs, const Entry &rhs) const
bool GetAllValues(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, TNamesDepend &a_values) const
Retrieve all values for a specific key.
Generic case-sensitive less than comparison.
virtual size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen)
Calculate the number of SI_CHAR required for converting the input from the storage format...
void SetMultiLine(bool a_bAllowMultiLine=true)
Should data values be permitted to span multiple lines in the file.
size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen) override
Calculate the number of SI_CHAR required for converting the input from the storage format...
File error (see errno for detail error)
Converter(const Converter &rhs)
SI_Error FindFileComment(SI_CHAR *&a_pData, bool a_bCopyStrings)
Parse the data looking for a file comment and store it if found.
CSimpleIniTempl< char, SI_GenericNoCase< char >, SI_ConvertA< char > > CSimpleIniA
interface definition for the OutputWriter object to pass to Save() in order to output the INI file da...
void SetMultiKey(bool a_bAllowMultiKey=true)
Should multiple identical keys be permitted in the file.
bool GetAllKeys(const SI_CHAR *a_pSection, TNamesDepend &a_names) const
Retrieve all unique key names in a section.
size_t SizeToStore(const SI_CHAR *a_pInputData)
Calculate the number of char required by the storage format of this data.
bool operator()(const Entry &lhs, const Entry &rhs) const
void Write(const char *a_pBuf) override
CSimpleIniTempl(const CSimpleIniTempl< SI_CHAR, SI_STRLESS, SI_CONVERTER > &o)
Copy.
SI_Error SetValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pValue, const SI_CHAR *a_pComment=nullptr)
Add or update a section or value.
bool IsMultiLine() const
Query the status of multi-line data.
void parse_process_var_define(ParseContext &pc, const std::string &var_name, const std::string &var_value)
Converter GetConverter() const
Return a conversion object to convert text to the same encoding as is used by the Save()...
GLsizei const GLchar ** string
SI_Error AddEntry(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pValue, const SI_CHAR *a_pComment, bool a_bCopyStrings)
Add the section/key/value to our data.
OutputWriter & operator=(const OutputWriter &)
int m_nOrder
Next order value, used to ensure sections and keys are output in the same order that they are loaded/...
bool operator()(const SI_CHAR *pLeft, const SI_CHAR *pRight) const
bool OutputMultiLineText(OutputWriter &a_oOutput, Converter &a_oConverter, const SI_CHAR *a_pText) const
MRPT custom INI file parser to allow minimal file preprocessing:
TNamesDepend m_strings
This vector stores allocated memory for copies of strings that have been supplied after the file load...
void compile(const std::string &expression, const std::map< std::string, double > &variables=std::map< std::string, double >(), const std::string &expr_name_for_error_reporting=std::string())
Initializes the object by compiling an expression.
bool m_bAllowMultiKey
Are multiple values permitted for the same key?
const TKeyVal * GetSection(const SI_CHAR *a_pSection) const
Retrieve all key and value pairs for a section.
bool IsNewLineChar(SI_CHAR a_c) const
An existing value was updated.
Null conversion class for MBCS/UTF-8 to char (or equivalent).
bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData, size_t a_uOutputDataSize)
Convert the input string to the storage format of this data.
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
std::map< std::string, double > defined_vars_values
size_t do_parse(const char *in_str, const size_t in_len, char *out_str)
Shared code for the two virtual methods.
double eval() const
Evaluates the current value of the precompiled formula.
void SkipNewLine(SI_CHAR *&a_pData) const
Skip over a newline character (or characters) for either DOS or UNIX.
Converter & operator=(const Converter &rhs)
SI_ConvertA & operator=(const SI_ConvertA &rhs)
bool IsLess(const SI_CHAR *a_pLeft, const SI_CHAR *a_pRight) const
Internal use of our string comparison function.
size_t m_uDataLen
Length of the data that we have stored.
Entry & operator=(const Entry &rhs)
std::multimap< Entry, const SI_CHAR *, typename Entry::KeyOrder > TKeyVal
map keys to values
bool IsSpace(SI_CHAR ch) const
Is the supplied character a whitespace character?
std::string trim(const std::string &str)
Removes leading and trailing spaces.
FILE * fopen(const char *fileName, const char *mode) noexcept
An OS-independent version of fopen.
typedef void(APIENTRYP PFNGLBLENDCOLORPROC)(GLclampf red
const SI_CHAR * m_pFileComment
File comment for this data, if one exists.
bool operator()(const SI_CHAR *pLeft, const SI_CHAR *pRight) const
int GetSectionSize(const SI_CHAR *a_pSection) const
Query the number of keys in a specific section.
bool FindEntry(SI_CHAR *&a_pData, const SI_CHAR *&a_pSection, const SI_CHAR *&a_pKey, const SI_CHAR *&a_pVal, const SI_CHAR *&a_pComment) const
Parse the data looking for the next valid entry.
SI_Error Load(std::istream &a_istream)
Load INI file data from an istream.
void Write(const char *a_pBuf)
bool operator<(const COccupancyGridMap2D::TPairLikelihoodIndex &e1, const COccupancyGridMap2D::TPairLikelihoodIndex &e2)
SI_ConvertA(const SI_ConvertA &rhs)
std::map< std::string, std::string > defined_vars
CSimpleIniTempl< SI_CHAR, SI_STRLESS, SI_CONVERTER > & operator=(const CSimpleIniTempl< SI_CHAR, SI_STRLESS, SI_CONVERTER > &o)
CSimpleIniTempl(bool a_bMultiKey=false, bool a_bMultiLine=false)
Default constructor.
MRPT_IniFileParser(const MRPT_IniFileParser &rhs)
bool LoadMultiLineText(SI_CHAR *&a_pData, const SI_CHAR *&a_pVal, const SI_CHAR *a_pTagName, bool a_bAllowBlankLinesInComment=false) const
OutputWriter class to write the INI data to an ostream.
std::list< Entry > TNamesDepend
set of dependent string pointers.
std::map< Entry, TKeyVal, typename Entry::KeyOrder > TSection
map sections to key/value map
SI_Error SaveFile(const char *a_pszFile) const
Save an INI file from memory to disk.
SI_CHAR * m_pData
Copy of the INI file data in our character format.
void memcpy(void *dest, size_t destSize, const void *src, size_t copyCount) noexcept
An OS and compiler independent version of "memcpy".
SI_Error Save(OutputWriter &a_oOutput) const
Save the INI data.
MRPT_IniFileParser & operator=(const MRPT_IniFileParser &rhs)
StreamWriter & operator=(const StreamWriter &)
std::string parse_process_var_eval(const ParseContext &pc, std::string expr)