summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2023-11-22 14:30:59 +1100
committerChris Johns <chrisj@rtems.org>2023-11-22 14:33:54 +1100
commit00af5a6ada64a1f5b3500c6f9fc17f1b393c2940 (patch)
tree81b278d2eef971214d058c63da96b1b7579cd1fe
parentrtemstoolkit/libierty: Update to gcc latest (diff)
downloadrtems-tools-00af5a6ada64a1f5b3500c6f9fc17f1b393c2940.tar.bz2
rtemstoolkit: Update SimpleIni to latest
The resolves C++17 warnings. Updates #4970
-rw-r--r--rtemstoolkit/SimpleIni.h598
1 files changed, 419 insertions, 179 deletions
diff --git a/rtemstoolkit/SimpleIni.h b/rtemstoolkit/SimpleIni.h
index fd37c4b..ec99abb 100644
--- a/rtemstoolkit/SimpleIni.h
+++ b/rtemstoolkit/SimpleIni.h
@@ -3,9 +3,9 @@
<table>
<tr><th>Library <td>SimpleIni
<tr><th>File <td>SimpleIni.h
- <tr><th>Author <td>Brodie Thiesfield [code at jellycan dot com]
+ <tr><th>Author <td>Brodie Thiesfield
<tr><th>Source <td>https://github.com/brofield/simpleini
- <tr><th>Version <td>4.17
+ <tr><th>Version <td>4.20
</table>
Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation.
@@ -20,7 +20,7 @@
@section features FEATURES
- MIT Licence allows free use in all software (including GPL and commercial)
- - multi-platform (Windows 95/98/ME/NT/2K/XP/2003, Windows CE, Linux, Unix)
+ - multi-platform (Windows CE/9x/NT..10/etc, Linux, MacOSX, Unix)
- loading and saving of INI-style configuration files
- configuration files can have any newline format on all platforms
- liberal acceptance of file format
@@ -42,22 +42,31 @@
- Windows/VC6 (warning level 3)
- Windows/VC.NET 2003 (warning level 4)
- Windows/VC 2005 (warning level 4)
+ - Windows/VC 2019 (warning level 4)
- Linux/gcc (-Wall)
@section usage USAGE SUMMARY
+ -# Decide if you will be using utf8 or MBCS files, and working with the
+ data in utf8, wchar_t or ICU chars.
+ -# If you will only be using straight utf8 files and access the data via the
+ char interface, then you do not need any conversion library and could define
+ SI_NO_CONVERSION. Note that no conversion also means no validation of the data.
+ If no converter is specified then the default converter is SI_CONVERT_GENERIC
+ on Mac/Linux and SI_CONVERT_WIN32 on Windows. If you need widechar support on
+ Mac/Linux then use either SI_CONVERT_GENERIC or SI_CONVERT_ICU. These are also
+ supported on all platforms.
-# Define the appropriate symbol for the converter you wish to use and
- include the SimpleIni.h header file. If no specific converter is defined
- then the default converter is used. The default conversion mode uses
- SI_CONVERT_WIN32 on Windows and SI_CONVERT_GENERIC on all other
- platforms. If you are using ICU then SI_CONVERT_ICU is supported on all
- platforms.
- -# Declare an instance the appropriate class. Note that the following
+ include the SimpleIni.h header file.
+ -# Declare an instance of the appropriate class. Note that the following
definitions are just shortcuts for commonly used types. Other types
(PRUnichar, unsigned short, unsigned char) are also possible.
<table>
- <tr><th>Interface <th>Case-sensitive <th>Load UTF-8 <th>Load MBCS <th>Typedef
+ <tr><th>Interface <th>Case-sensitive <th>Load UTF-8 <th>Load MBCS <th>Typedef
+ <tr><th>SI_NO_CONVERSION
+ <tr><td>char <td>No <td>Yes <td>No <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>Yes <td>No <td>CSimpleIniCaseA
<tr><th>SI_CONVERT_GENERIC
<tr><td>char <td>No <td>Yes <td>Yes #1 <td>CSimpleIniA
<tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
@@ -88,6 +97,8 @@
<tr><td>GetValue <td>Return a value for a section & key
<tr><td>SetValue <td>Add or update a value for a section & key
<tr><td>Delete <td>Remove a section, or a key from a section
+ <tr><td>SectionExists <td>Does a section exist?
+ <tr><td>KeyExists <td>Does a key exist?
</table>
-# Call Save() or SaveFile() to save the INI configuration data
@@ -161,9 +172,10 @@
SI_STRLESS class, or by sorting the strings external to this library.
- Usage of the <mbstring.h> header on Windows can be disabled by defining
SI_NO_MBCS. This is defined automatically on Windows CE platforms.
+ - Not thread-safe so manage your own locking
@section contrib CONTRIBUTIONS
-
+
- 2010/05/03: Tobias Gehrig: added GetDoubleValue()
@section licence MIT LICENCE
@@ -213,11 +225,11 @@
#endif
#include <cstring>
+#include <cstdlib>
#include <string>
#include <map>
#include <list>
#include <algorithm>
-#include <stdlib.h>
#include <stdio.h>
#ifdef SI_SUPPORT_IOSTREAMS
@@ -233,16 +245,16 @@
# define SI_ASSERT(x)
#endif
-enum SI_Error {
- SI_OK = 0, //!< No error
- SI_UPDATED = 1, //!< An existing value was updated
- SI_INSERTED = 2, //!< A new value was inserted
+using SI_Error = int;
- // note: test for any error with (retval < 0)
- SI_FAIL = -1, //!< Generic failure
- SI_NOMEM = -2, //!< Out of memory error
- SI_FILE = -3 //!< File error (see errno for detail error)
-};
+constexpr int SI_OK = 0; //!< No error
+constexpr int SI_UPDATED = 1; //!< An existing value was updated
+constexpr int SI_INSERTED = 2; //!< A new value was inserted
+
+// note: test for any error with (retval < 0)
+constexpr int SI_FAIL = -1; //!< Generic failure
+constexpr int SI_NOMEM = -2; //!< Out of memory error
+constexpr int SI_FILE = -3; //!< File error (see errno for detail error)
#define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
@@ -327,7 +339,7 @@ public:
#endif
/** Strict less ordering by name of key only */
- struct KeyOrder : std::binary_function<Entry, Entry, bool> {
+ struct KeyOrder {
bool operator()(const Entry & lhs, const Entry & rhs) const {
const static SI_STRLESS isLess = SI_STRLESS();
return isLess(lhs.pItem, rhs.pItem);
@@ -335,7 +347,7 @@ public:
};
/** Strict less ordering by order, and then name of key */
- struct LoadOrder : std::binary_function<Entry, Entry, bool> {
+ struct LoadOrder {
bool operator()(const Entry & lhs, const Entry & rhs) const {
if (lhs.nOrder != rhs.nOrder) {
return lhs.nOrder < rhs.nOrder;
@@ -529,7 +541,7 @@ public:
bool IsMultiLine() const { return m_bAllowMultiLine; }
/** Should spaces be added around the equals sign when writing key/value
- pairs out. When true, the result will be "key = value". When false,
+ pairs out. When true, the result will be "key = value". When false,
the result will be "key=value". This value may be changed at any time.
\param a_bSpaces Add spaces around the equals sign?
@@ -540,6 +552,35 @@ public:
/** Query the status of spaces output */
bool UsingSpaces() const { return m_bSpaces; }
+
+
+ /** Should we recognise and parse quotes in single line values?
+
+ \param a_bParseQuotes Parse quoted data in values?
+ */
+ void SetQuotes(bool a_bParseQuotes = true) {
+ m_bParseQuotes = a_bParseQuotes;
+ }
+
+ /** Are we permitting keys and values to be quoted? */
+ bool UsingQuotes() const { return m_bParseQuotes; }
+
+ /** When reading/writing an ini file, do we require every key to have an equals
+ sign to delineate a valid key value. If false, then every valid key must
+ have an equals sign and any lines without an equals sign is ignored. If
+ true then keys do not require an equals sign to be considered a key. Note
+ that this means that any non-commented line of text would become a key.
+
+ \param a_bAllowKeyOnly Permit keys without an equals sign or value.
+ */
+ void SetAllowKeyOnly(bool a_bAllowKeyOnly = true) {
+ m_bAllowKeyOnly = a_bAllowKeyOnly;
+ }
+
+ /** Do we allow keys to exist without a value or equals sign? */
+ bool GetAllowKeyOnly() const { return m_bAllowKeyOnly; }
+
+
/*-----------------------------------------------------------------------*/
/** @}
@@ -770,8 +811,8 @@ public:
) const;
/** Retrieve all unique key names in a section. The sort order of the
- returned strings is NOT DEFINED. You can sort the names into the load
- order if desired. Search this file for ".sort" for an example. Only
+ returned strings is NOT DEFINED. You can sort the names into the load
+ order if desired. Search this file for ".sort" for an example. Only
unique key names are returned.
NOTE! This structure contains only pointers to strings. The actual
@@ -792,8 +833,8 @@ public:
) const;
/** Retrieve all values for a specific key. This method can be used when
- multiple keys are both enabled and disabled. Note that the sort order
- of the returned strings is NOT DEFINED. You can sort the names into
+ multiple keys are both enabled and disabled. Note that the sort order
+ of the returned strings is NOT DEFINED. You can sort the names into
the load order if desired. Search this file for ".sort" for an example.
NOTE! The returned values are pointers to string data stored in memory
@@ -837,13 +878,27 @@ public:
are in use!
@param a_pSection Name of the section to return
- @return boolean Was a section matching the supplied
- name found.
+ @return Section data
*/
const TKeyVal * GetSection(
const SI_CHAR * a_pSection
) const;
+ /** Test if a section exists. Convenience function */
+ inline bool SectionExists(
+ const SI_CHAR * a_pSection
+ ) const {
+ return GetSection(a_pSection) != NULL;
+ }
+
+ /** Test if the key exists in a section. Convenience function. */
+ inline bool KeyExists(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey
+ ) const {
+ return GetValue(a_pSection, a_pKey) != NULL;
+ }
+
/** Retrieve the value for a specific key. If multiple keys are enabled
(see SetMultiKey) then only the first value associated with that key
will be returned, see GetAllValues for getting all values with multikey.
@@ -914,7 +969,7 @@ public:
Strings starting with "t", "y", "on" or "1" are returned as logically true.
Strings starting with "f", "n", "of" or "0" are returned as logically false.
- For all other values the default is returned. Character comparisons are
+ For all other values the default is returned. Character comparisons are
case-insensitive.
@param a_pSection Section to search
@@ -953,9 +1008,9 @@ public:
character starting every line).
@param a_bForceReplace Should all existing values in a multi-key INI
file be replaced with this entry. This option has
- no effect if not using multi-key files. The
+ no effect if not using multi-key files. The
difference between Delete/SetValue and SetValue
- with a_bForceReplace = true, is that the load
+ with a_bForceReplace = true, is that the load
order and comment will be preserved this way.
@return SI_Error See error definitions
@@ -977,19 +1032,19 @@ public:
when multiple keys are enabled.
@param a_pSection Section to add or update
- @param a_pKey Key to add or update.
- @param a_nValue Value to set.
- @param a_pComment Comment to be associated with the key. See the
+ @param a_pKey Key to add or update.
+ @param a_nValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
notes on SetValue() for comments.
- @param a_bUseHex By default the value will be written to the file
- in decimal format. Set this to true to write it
+ @param a_bUseHex By default the value will be written to the file
+ in decimal format. Set this to true to write it
as hexadecimal.
@param a_bForceReplace Should all existing values in a multi-key INI
file be replaced with this entry. This option has
- no effect if not using multi-key files. The
- difference between Delete/SetLongValue and
- SetLongValue with a_bForceReplace = true, is that
- the load order and comment will be preserved this
+ no effect if not using multi-key files. The
+ difference between Delete/SetLongValue and
+ SetLongValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
way.
@return SI_Error See error definitions
@@ -1009,16 +1064,16 @@ public:
when multiple keys are enabled.
@param a_pSection Section to add or update
- @param a_pKey Key to add or update.
- @param a_nValue Value to set.
- @param a_pComment Comment to be associated with the key. See the
+ @param a_pKey Key to add or update.
+ @param a_nValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
notes on SetValue() for comments.
@param a_bForceReplace Should all existing values in a multi-key INI
file be replaced with this entry. This option has
- no effect if not using multi-key files. The
- difference between Delete/SetDoubleValue and
- SetDoubleValue with a_bForceReplace = true, is that
- the load order and comment will be preserved this
+ no effect if not using multi-key files. The
+ difference between Delete/SetDoubleValue and
+ SetDoubleValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
way.
@return SI_Error See error definitions
@@ -1037,16 +1092,16 @@ public:
when multiple keys are enabled.
@param a_pSection Section to add or update
- @param a_pKey Key to add or update.
- @param a_bValue Value to set.
- @param a_pComment Comment to be associated with the key. See the
+ @param a_pKey Key to add or update.
+ @param a_bValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
notes on SetValue() for comments.
@param a_bForceReplace Should all existing values in a multi-key INI
file be replaced with this entry. This option has
- no effect if not using multi-key files. The
- difference between Delete/SetBoolValue and
- SetBoolValue with a_bForceReplace = true, is that
- the load order and comment will be preserved this
+ no effect if not using multi-key files. The
+ difference between Delete/SetBoolValue and
+ SetBoolValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
way.
@return SI_Error See error definitions
@@ -1065,8 +1120,8 @@ public:
data returned by GetSection is invalid and must not be used after
anything has been deleted from that section using this method.
Note when multiple keys is enabled, this will delete all keys with
- that name; there is no way to selectively delete individual key/values
- in this situation.
+ that name; to selectively delete individual key/values, use
+ DeleteValue.
@param a_pSection Section to delete key from, or if
a_pKey is NULL, the section to remove.
@@ -1085,6 +1140,33 @@ public:
bool a_bRemoveEmpty = false
);
+ /** Delete an entire section, or a key from a section. If value is
+ provided, only remove keys with the value. Note that the data
+ returned by GetSection is invalid and must not be used after
+ anything has been deleted from that section using this method.
+ Note when multiple keys is enabled, all keys with the value will
+ be deleted.
+
+ @param a_pSection Section to delete key from, or if
+ a_pKey is NULL, the section to remove.
+ @param a_pKey Key to remove from the section. Set to
+ NULL to remove the entire section.
+ @param a_pValue Value of key to remove from the section.
+ Set to NULL to remove all keys.
+ @param a_bRemoveEmpty If the section is empty after this key has
+ been deleted, should the empty section be
+ removed?
+
+ @return true Key/value or section was deleted.
+ @return false Key/value or section was not found.
+ */
+ bool DeleteValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pValue,
+ bool a_bRemoveEmpty = false
+ );
+
/*-----------------------------------------------------------------------*/
/** @}
@{ @name Converter */
@@ -1140,9 +1222,9 @@ private:
comment character starting every line).
@param a_bForceReplace Should all existing values in a multi-key INI
file be replaced with this entry. This option has
- no effect if not using multi-key files. The
+ no effect if not using multi-key files. The
difference between Delete/AddEntry and AddEntry
- with a_bForceReplace = true, is that the load
+ with a_bForceReplace = true, is that the load
order and comment will be preserved this way.
@param a_bCopyStrings Should copies of the strings be made or not.
If false then the pointers will be used as is.
@@ -1186,6 +1268,7 @@ private:
bool IsMultiLineTag(const SI_CHAR * a_pData) const;
bool IsMultiLineData(const SI_CHAR * a_pData) const;
+ bool IsSingleLineQuotedValue(const SI_CHAR* a_pData) const;
bool LoadMultiLineText(
SI_CHAR *& a_pData,
const SI_CHAR *& a_pVal,
@@ -1217,6 +1300,9 @@ private:
/** File comment for this data, if one exists. */
const SI_CHAR * m_pFileComment;
+ /** constant empty string */
+ const SI_CHAR m_cEmptyString;
+
/** Parsed INI data. Section -> (Key -> Value). */
TSection m_data;
@@ -1237,6 +1323,12 @@ private:
/** Should spaces be written out surrounding the equals sign? */
bool m_bSpaces;
+
+ /** Should quoted data in values be recognized and parsed? */
+ bool m_bParseQuotes;
+
+ /** Do keys always need to have an equals sign when reading/writing? */
+ bool m_bAllowKeyOnly;
/** Next order value, used to ensure sections and keys are output in the
same order that they are loaded/added.
@@ -1257,10 +1349,13 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl(
: m_pData(0)
, m_uDataLen(0)
, m_pFileComment(NULL)
+ , m_cEmptyString(0)
, m_bStoreIsUtf8(a_bIsUtf8)
, m_bAllowMultiKey(a_bAllowMultiKey)
, m_bAllowMultiLine(a_bAllowMultiLine)
, m_bSpaces(true)
+ , m_bParseQuotes(false)
+ , m_bAllowKeyOnly(false)
, m_nOrder(0)
{ }
@@ -1357,14 +1452,14 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
if (lSize == 0) {
return SI_OK;
}
-
+
// allocate and ensure NULL terminated
- char * pData = new char[lSize+1];
+ char * pData = new(std::nothrow) char[lSize+static_cast<size_t>(1)];
if (!pData) {
return SI_NOMEM;
}
pData[lSize] = 0;
-
+
// load data into buffer
fseek(a_fpFile, 0, SEEK_SET);
size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
@@ -1386,21 +1481,26 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(
size_t a_uDataLen
)
{
- SI_CONVERTER converter(m_bStoreIsUtf8);
-
- if (a_uDataLen == 0) {
+ if (!a_pData) {
return SI_OK;
}
+
+ // if the UTF-8 BOM exists, consume it and set mode to unicode, if we have
+ // already loaded data and try to change mode half-way through then this will
+ // be ignored and we will assert in debug versions
+ if (a_uDataLen >= 3 && memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
+ a_pData += 3;
+ a_uDataLen -= 3;
+ SI_ASSERT(m_bStoreIsUtf8 || !m_pData); // we don't expect mixed mode data
+ SetUnicode();
+ }
- // consume the UTF-8 BOM if it exists
- if (m_bStoreIsUtf8 && a_uDataLen >= 3) {
- if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
- a_pData += 3;
- a_uDataLen -= 3;
- }
+ if (a_uDataLen == 0) {
+ return SI_OK;
}
// determine the length of the converted data
+ SI_CONVERTER converter(m_bStoreIsUtf8);
size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
if (uLen == (size_t)(-1)) {
return SI_FAIL;
@@ -1408,7 +1508,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(
// allocate memory for the data, ensure that there is a NULL
// terminator wherever the converted data ends
- SI_CHAR * pData = new SI_CHAR[uLen+1];
+ SI_CHAR * pData = new(std::nothrow) SI_CHAR[uLen+1];
if (!pData) {
return SI_NOMEM;
}
@@ -1512,6 +1612,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
{
a_pComment = NULL;
+ bool bHaveValue = false;
SI_CHAR * pTrail = NULL;
while (*a_pData) {
// skip spaces and empty lines
@@ -1569,19 +1670,20 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
}
// find the end of the key name (it may contain spaces)
- // and convert it to lowercase as necessary
a_pKey = a_pData;
while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
++a_pData;
}
+ // *a_pData is null, equals, or newline
- // if it's an invalid line, just skip it
- if (*a_pData != '=') {
+ // if no value and we don't allow no value, then invalid
+ bHaveValue = (*a_pData == '=');
+ if (!bHaveValue && !m_bAllowKeyOnly) {
continue;
}
// empty keys are invalid
- if (a_pKey == a_pData) {
+ if (bHaveValue && a_pKey == a_pData) {
while (*a_pData && !IsNewLineChar(*a_pData)) {
++a_pData;
}
@@ -1594,36 +1696,56 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
--pTrail;
}
++pTrail;
- *pTrail = 0;
- // skip leading whitespace on the value
- ++a_pData; // safe as checked that it == '=' above
- while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
- ++a_pData;
- }
+ if (bHaveValue) {
+ // process the value
+ *pTrail = 0;
- // find the end of the value which is the end of this line
- a_pVal = a_pData;
- while (*a_pData && !IsNewLineChar(*a_pData)) {
- ++a_pData;
- }
+ // skip leading whitespace on the value
+ ++a_pData; // safe as checked that it == '=' above
+ while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
+ ++a_pData;
+ }
- // remove trailing spaces from the value
- pTrail = a_pData - 1;
- if (*a_pData) { // prepare for the next round
- SkipNewLine(a_pData);
- }
- while (pTrail >= a_pVal && IsSpace(*pTrail)) {
- --pTrail;
- }
- ++pTrail;
- *pTrail = 0;
+ // find the end of the value which is the end of this line
+ a_pVal = a_pData;
+ while (*a_pData && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ // remove trailing spaces from the value
+ pTrail = a_pData - 1;
+ if (*a_pData) { // prepare for the next round
+ SkipNewLine(a_pData);
+ }
+ while (pTrail >= a_pVal && IsSpace(*pTrail)) {
+ --pTrail;
+ }
+ ++pTrail;
+ *pTrail = 0;
- // check for multi-line entries
- if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
- // skip the "<<<" to get the tag that will end the multiline
- const SI_CHAR * pTagName = a_pVal + 3;
- return LoadMultiLineText(a_pData, a_pVal, pTagName);
+ // check for multi-line entries
+ if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
+ // skip the "<<<" to get the tag that will end the multiline
+ const SI_CHAR* pTagName = a_pVal + 3;
+ return LoadMultiLineText(a_pData, a_pVal, pTagName);
+ }
+
+ // check for quoted values, we are not supporting escapes in quoted values (yet)
+ if (m_bParseQuotes) {
+ --pTrail;
+ if (pTrail > a_pVal && *a_pVal == '"' && *pTrail == '"') {
+ ++a_pVal;
+ *pTrail = 0;
+ }
+ }
+ }
+ else {
+ // no value to process, just prepare for the next
+ if (*a_pData) {
+ SkipNewLine(a_pData);
+ }
+ *pTrail = 0;
}
// return the standard entry
@@ -1685,6 +1807,41 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData(
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
bool
+CSimpleIniTempl<SI_CHAR, SI_STRLESS, SI_CONVERTER>::IsSingleLineQuotedValue(
+ const SI_CHAR* a_pData
+) const
+{
+ // data needs quoting if it starts or ends with whitespace
+ // and doesn't have embedded newlines
+
+ // empty string
+ if (!*a_pData) {
+ return false;
+ }
+
+ // check for prefix
+ if (IsSpace(*a_pData)) {
+ return true;
+ }
+
+ // embedded newlines
+ while (*a_pData) {
+ if (IsNewLineChar(*a_pData)) {
+ return false;
+ }
+ ++a_pData;
+ }
+
+ // check for suffix
+ if (IsSpace(*--a_pData)) {
+ return true;
+ }
+
+ return false;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar(
SI_CHAR a_c
) const
@@ -1716,8 +1873,8 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
a_pVal = a_pData;
// find the end tag. This tag must start in column 1 and be
- // followed by a newline. No whitespace removal is done while
- // searching for this tag.
+ // followed by a newline. We ignore any whitespace after the end
+ // tag but not whitespace before it.
SI_CHAR cEndOfLineChar = *a_pData;
for(;;) {
// if we are loading comments then we need a comment character as
@@ -1773,10 +1930,18 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
// if are looking for a tag then do the check now. This is done before
// checking for end of the data, so that if we have the tag at the end
// of the data then the tag is removed correctly.
- if (a_pTagName &&
- (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)))
- {
- break;
+ if (a_pTagName) {
+ // strip whitespace from the end of this tag
+ SI_CHAR* pc = a_pData - 1;
+ while (pc > pDataLine && IsSpace(*pc)) --pc;
+ SI_CHAR ch = *++pc;
+ *pc = 0;
+
+ if (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)) {
+ break;
+ }
+
+ *pc = ch;
}
// if we are at the end of the data then we just automatically end
@@ -1833,7 +1998,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString(
for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
}
++uLen; // NULL character
- SI_CHAR * pCopy = new SI_CHAR[uLen];
+ SI_CHAR * pCopy = new(std::nothrow) SI_CHAR[uLen];
if (!pCopy) {
return SI_NOMEM;
}
@@ -1878,7 +2043,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
// only set the comment if this is a section only entry
Entry oSection(a_pSection, ++m_nOrder);
- if (a_pComment && (!a_pKey || !a_pValue)) {
+ if (a_pComment && !a_pKey) {
oSection.pComment = a_pComment;
}
@@ -1888,14 +2053,15 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
iSection = i.first;
bInserted = true;
}
- if (!a_pKey || !a_pValue) {
- // section only entries are specified with pItem and pVal as NULL
+ if (!a_pKey) {
+ // section only entries are specified with pItem as NULL
return bInserted ? SI_INSERTED : SI_UPDATED;
}
// check for existence of the key
TKeyVal & keyval = iSection->second;
typename TKeyVal::iterator iKey = keyval.find(a_pKey);
+ bInserted = iKey == keyval.end();
// remove all existing entries but save the load order and
// comment of the first entry
@@ -1918,6 +2084,11 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
iKey = keyval.end();
}
+ // values need to be a valid string, even if they are an empty string
+ if (!a_pValue) {
+ a_pValue = &m_cEmptyString;
+ }
+
// make string copies if necessary
bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
if (a_bCopyStrings) {
@@ -1942,8 +2113,8 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
}
typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL));
iKey = keyval.insert(oEntry);
- bInserted = true;
}
+
iKey->second = a_pValue;
return bInserted ? SI_INSERTED : SI_UPDATED;
}
@@ -2009,23 +2180,23 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetLongValue(
long nValue = a_nDefault;
char * pszSuffix = szValue;
if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
- if (!szValue[2]) return a_nDefault;
- nValue = ::strtol(&szValue[2], &pszSuffix, 16);
+ if (!szValue[2]) return a_nDefault;
+ nValue = strtol(&szValue[2], &pszSuffix, 16);
}
else {
- nValue = ::strtol(szValue, &pszSuffix, 10);
+ nValue = strtol(szValue, &pszSuffix, 10);
}
// any invalid strings will return the default value
- if (*pszSuffix) {
- return a_nDefault;
+ if (*pszSuffix) {
+ return a_nDefault;
}
return nValue;
}
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
-SI_Error
+SI_Error
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue(
const SI_CHAR * a_pSection,
const SI_CHAR * a_pKey,
@@ -2043,13 +2214,13 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue(
#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
#else // !__STDC_WANT_SECURE_LIB__
- sprintf(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
+ snprintf(szInput, sizeof(szInput), a_bUseHex ? "0x%lx" : "%ld", a_nValue);
#endif // __STDC_WANT_SECURE_LIB__
// convert to output text
SI_CHAR szOutput[64];
SI_CONVERTER c(m_bStoreIsUtf8);
- c.ConvertFromStore(szInput, strlen(szInput) + 1,
+ c.ConvertFromStore(szInput, strlen(szInput) + 1,
szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
// actually add it
@@ -2080,42 +2251,42 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetDoubleValue(
double nValue = strtod(szValue, &pszSuffix);
// any invalid strings will return the default value
- if (!pszSuffix || *pszSuffix) {
- return a_nDefault;
+ if (!pszSuffix || *pszSuffix) {
+ return a_nDefault;
}
return nValue;
}
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
-SI_Error
+SI_Error
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetDoubleValue(
- const SI_CHAR * a_pSection,
- const SI_CHAR * a_pKey,
- double a_nValue,
- const SI_CHAR * a_pComment,
- bool a_bForceReplace
- )
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ double a_nValue,
+ const SI_CHAR * a_pComment,
+ bool a_bForceReplace
+ )
{
- // use SetValue to create sections
- if (!a_pSection || !a_pKey) return SI_FAIL;
+ // use SetValue to create sections
+ if (!a_pSection || !a_pKey) return SI_FAIL;
- // convert to an ASCII string
- char szInput[64];
+ // convert to an ASCII string
+ char szInput[64];
#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
- sprintf_s(szInput, "%f", a_nValue);
+ sprintf_s(szInput, "%f", a_nValue);
#else // !__STDC_WANT_SECURE_LIB__
- sprintf(szInput, "%f", a_nValue);
+ snprintf(szInput, sizeof(szInput), "%f", a_nValue);
#endif // __STDC_WANT_SECURE_LIB__
- // convert to output text
- SI_CHAR szOutput[64];
- SI_CONVERTER c(m_bStoreIsUtf8);
- c.ConvertFromStore(szInput, strlen(szInput) + 1,
- szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
+ // convert to output text
+ SI_CHAR szOutput[64];
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ c.ConvertFromStore(szInput, strlen(szInput) + 1,
+ szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
- // actually add it
- return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
+ // actually add it
+ return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
}
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
@@ -2154,7 +2325,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetBoolValue(
}
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
-SI_Error
+SI_Error
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetBoolValue(
const SI_CHAR * a_pSection,
const SI_CHAR * a_pKey,
@@ -2172,13 +2343,13 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetBoolValue(
// convert to output text
SI_CHAR szOutput[64];
SI_CONVERTER c(m_bStoreIsUtf8);
- c.ConvertFromStore(pszInput, strlen(pszInput) + 1,
+ c.ConvertFromStore(pszInput, strlen(pszInput) + 1,
szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
// actually add it
return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
}
-
+
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
bool
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues(
@@ -2390,6 +2561,19 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
oSections.sort(typename Entry::LoadOrder());
#endif
+ // if there is an empty section name, then it must be written out first
+ // regardless of the load order
+ typename TNamesDepend::iterator is = oSections.begin();
+ for (; is != oSections.end(); ++is) {
+ if (!*is->pItem) {
+ // move the empty section name to the front of the section list
+ if (is != oSections.begin()) {
+ oSections.splice(oSections.begin(), oSections, is, std::next(is));
+ }
+ break;
+ }
+ }
+
// write the file comment if we have one
bool bNeedNewLine = false;
if (m_pFileComment) {
@@ -2465,22 +2649,31 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
}
a_oOutput.Write(convert.Data());
- // write the value
- if (!convert.ConvertToStore(iValue->pItem)) {
- return SI_FAIL;
- }
- a_oOutput.Write(m_bSpaces ? " = " : "=");
- if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
- // multi-line data needs to be processed specially to ensure
- // that we use the correct newline format for the current system
- a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
- if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
+ // write the value as long
+ if (*iValue->pItem || !m_bAllowKeyOnly) {
+ if (!convert.ConvertToStore(iValue->pItem)) {
return SI_FAIL;
}
- a_oOutput.Write("END_OF_TEXT");
- }
- else {
- a_oOutput.Write(convert.Data());
+ a_oOutput.Write(m_bSpaces ? " = " : "=");
+ if (m_bParseQuotes && IsSingleLineQuotedValue(iValue->pItem)) {
+ // the only way to preserve external whitespace on a value (i.e. before or after)
+ // is to quote it. This is simple quoting, we don't escape quotes within the data.
+ a_oOutput.Write("\"");
+ a_oOutput.Write(convert.Data());
+ a_oOutput.Write("\"");
+ }
+ else if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
+ // multi-line data needs to be processed specially to ensure
+ // that we use the correct newline format for the current system
+ a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
+ if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write("END_OF_TEXT");
+ }
+ else {
+ a_oOutput.Write(convert.Data());
+ }
}
a_oOutput.Write(SI_NEWLINE_A);
}
@@ -2529,6 +2722,18 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(
bool a_bRemoveEmpty
)
{
+ return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty);
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pValue,
+ bool a_bRemoveEmpty
+ )
+{
if (!a_pSection) {
return false;
}
@@ -2545,18 +2750,30 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(
return false;
}
+ const static SI_STRLESS isLess = SI_STRLESS();
+
// remove any copied strings and then the key
typename TKeyVal::iterator iDelete;
+ bool bDeleted = false;
do {
iDelete = iKeyVal++;
- DeleteString(iDelete->first.pItem);
- DeleteString(iDelete->second);
- iSection->second.erase(iDelete);
+ if(a_pValue == NULL ||
+ (isLess(a_pValue, iDelete->second) == false &&
+ isLess(iDelete->second, a_pValue) == false)) {
+ DeleteString(iDelete->first.pItem);
+ DeleteString(iDelete->second);
+ iSection->second.erase(iDelete);
+ bDeleted = true;
+ }
}
while (iKeyVal != iSection->second.end()
&& !IsLess(a_pKey, iKeyVal->first.pItem));
+ if(!bDeleted) {
+ return false;
+ }
+
// done now if the section is not empty or we are not pruning away
// the empty sections. Otherwise let it fall through into the section
// deletion code
@@ -2610,13 +2827,15 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString(
// SimpleIni.h, set the converter that you wish you use by defining one of the
// following symbols.
//
+// SI_NO_CONVERSION Do not make the "W" wide character version of the
+// library available. Only CSimpleIniA etc is defined.
// SI_CONVERT_GENERIC Use the Unicode reference conversion library in
// the accompanying files ConvertUTF.h/c
// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
// ICU headers on include path and icuuc.lib
// SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
-#if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
+#if !defined(SI_NO_CONVERSION) && !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
# ifdef _WIN32
# define SI_CONVERT_WIN32
# else
@@ -2873,7 +3092,7 @@ public:
// This uses the Unicode reference implementation to do the
// conversion from UTF-8 to wchar_t. The required files are
// ConvertUTF.h and ConvertUTF.c which should be included in
- // the distribution but are publically available from unicode.org
+ // the distribution but are publicly available from unicode.org
// at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
ConversionResult retval;
const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
@@ -2960,7 +3179,7 @@ public:
// This uses the Unicode reference implementation to do the
// conversion from wchar_t to UTF-8. The required files are
// ConvertUTF.h and ConvertUTF.c which should be included in
- // the distribution but are publically available from unicode.org
+ // the distribution but are publicly available from unicode.org
// at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
ConversionResult retval;
UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
@@ -3346,6 +3565,19 @@ public:
#endif // SI_CONVERT_WIN32
+
+// ---------------------------------------------------------------------------
+// SI_NO_CONVERSION
+// ---------------------------------------------------------------------------
+#ifdef SI_NO_CONVERSION
+
+#define SI_Case SI_GenericCase
+#define SI_NoCase SI_GenericNoCase
+
+#endif // SI_NO_CONVERSION
+
+
+
// ---------------------------------------------------------------------------
// TYPE DEFINITIONS
// ---------------------------------------------------------------------------
@@ -3355,27 +3587,35 @@ typedef CSimpleIniTempl<char,
typedef CSimpleIniTempl<char,
SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA;
-#if defined(SI_CONVERT_ICU)
+#if defined(SI_NO_CONVERSION)
+// if there is no wide char conversion then we don't need to define the
+// widechar "W" versions of CSimpleIni
+# define CSimpleIni CSimpleIniA
+# define CSimpleIniCase CSimpleIniCaseA
+# define SI_NEWLINE SI_NEWLINE_A
+#else
+# if defined(SI_CONVERT_ICU)
typedef CSimpleIniTempl<UChar,
SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;
typedef CSimpleIniTempl<UChar,
SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;
-#else
+# else
typedef CSimpleIniTempl<wchar_t,
SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;
typedef CSimpleIniTempl<wchar_t,
SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;
-#endif
+# endif
-#ifdef _UNICODE
-# define CSimpleIni CSimpleIniW
-# define CSimpleIniCase CSimpleIniCaseW
-# define SI_NEWLINE SI_NEWLINE_W
-#else // !_UNICODE
-# define CSimpleIni CSimpleIniA
-# define CSimpleIniCase CSimpleIniCaseA
-# define SI_NEWLINE SI_NEWLINE_A
-#endif // _UNICODE
+# ifdef _UNICODE
+# define CSimpleIni CSimpleIniW
+# define CSimpleIniCase CSimpleIniCaseW
+# define SI_NEWLINE SI_NEWLINE_W
+# else // !_UNICODE
+# define CSimpleIni CSimpleIniA
+# define CSimpleIniCase CSimpleIniCaseA
+# define SI_NEWLINE SI_NEWLINE_A
+# endif // _UNICODE
+#endif
#ifdef _MSC_VER
# pragma warning (pop)