3rdparty: Update simpleini to v4.22

This commit is contained in:
JordanTheToaster 2024-05-04 13:40:10 +01:00 committed by Connor McLaughlin
parent 90338ed065
commit 6630783686
4 changed files with 313 additions and 122 deletions

View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2006-2013 Brodie Thiesfield Copyright (c) 2006-2022 Brodie Thiesfield
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in

View File

@ -24,7 +24,7 @@
Conversions between UTF32, UTF-16, and UTF-8. Header file. Conversions between UTF32, UTF-16, and UTF-8. Header file.
Several funtions are included here, forming a complete set of Several functions are included here, forming a complete set of
conversions between the three formats. UTF-7 is not included conversions between the three formats. UTF-7 is not included
here, but is handled in a separate source file. here, but is handled in a separate source file.
@ -102,7 +102,7 @@ typedef unsigned char Boolean; /* 0 or 1 */
typedef enum { typedef enum {
conversionOK, /* conversion successful */ conversionOK, /* conversion successful */
sourceExhausted, /* partial character in source, but hit end */ sourceExhausted, /* partial character in source, but hit end */
targetExhausted, /* insuff. room in target for conversion */ targetExhausted, /* insufficient room in target for conversion */
sourceIllegal /* source sequence is illegal/malformed */ sourceIllegal /* source sequence is illegal/malformed */
} ConversionResult; } ConversionResult;

View File

@ -3,9 +3,9 @@
<table> <table>
<tr><th>Library <td>SimpleIni <tr><th>Library <td>SimpleIni
<tr><th>File <td>SimpleIni.h <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>Source <td>https://github.com/brofield/simpleini
<tr><th>Version <td>4.17 <tr><th>Version <td>4.22
</table> </table>
Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation. Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation.
@ -16,7 +16,6 @@
Windows and Linux/Unix. It is fast, simple and source code using this Windows and Linux/Unix. It is fast, simple and source code using this
component will compile unchanged on either OS. component will compile unchanged on either OS.
@section features FEATURES @section features FEATURES
- MIT Licence allows free use in all software (including GPL and commercial) - MIT Licence allows free use in all software (including GPL and commercial)
@ -38,26 +37,36 @@
- support for non-standard character types or file encodings - support for non-standard character types or file encodings
via user-written converter classes via user-written converter classes
- support for adding/modifying values programmatically - support for adding/modifying values programmatically
- compiles cleanly in the following compilers: - should compile cleanly without warning usually at the strictest warning level
- it has been tested with the following compilers:
- Windows/VC6 (warning level 3) - Windows/VC6 (warning level 3)
- Windows/VC.NET 2003 (warning level 4) - Windows/VC.NET 2003 (warning level 4)
- Windows/VC 2005 (warning level 4) - Windows/VC 2005 (warning level 4)
- Windows/VC 2019 (warning level 4)
- Linux/gcc (-Wall) - Linux/gcc (-Wall)
- Mac OS/c++ (-Wall)
@section usage USAGE SUMMARY @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 -# Define the appropriate symbol for the converter you wish to use and
include the SimpleIni.h header file. If no specific converter is defined include the SimpleIni.h header file.
then the default converter is used. The default conversion mode uses -# Declare an instance of the appropriate class. Note that the following
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
definitions are just shortcuts for commonly used types. Other types definitions are just shortcuts for commonly used types. Other types
(PRUnichar, unsigned short, unsigned char) are also possible. (PRUnichar, unsigned short, unsigned char) are also possible.
<table> <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><th>SI_CONVERT_GENERIC
<tr><td>char <td>No <td>Yes <td>Yes #1 <td>CSimpleIniA <tr><td>char <td>No <td>Yes <td>Yes #1 <td>CSimpleIniA
<tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA <tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
@ -77,6 +86,8 @@
#1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.<br> #1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.<br>
#2 Only affects Windows. On Windows this uses MBCS functions and #2 Only affects Windows. On Windows this uses MBCS functions and
so may fold case incorrectly leading to uncertain results. so may fold case incorrectly leading to uncertain results.
-# Set all the options that you require, see all the Set*() options below.
The SetUnicode() option is very common and can be specified in the constructor.
-# Call LoadData() or LoadFile() to load and parse the INI configuration file -# Call LoadData() or LoadFile() to load and parse the INI configuration file
-# Access and modify the data of the file using the following functions -# Access and modify the data of the file using the following functions
<table> <table>
@ -88,6 +99,8 @@
<tr><td>GetValue <td>Return a value for a section & key <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>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>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> </table>
-# Call Save() or SaveFile() to save the INI configuration data -# Call Save() or SaveFile() to save the INI configuration data
@ -164,15 +177,18 @@
- Not thread-safe so manage your own locking - Not thread-safe so manage your own locking
@section contrib CONTRIBUTIONS @section contrib CONTRIBUTIONS
Many thanks to the following contributors:
- 2010/05/03: Tobias Gehrig: added GetDoubleValue() - 2010/05/03: Tobias Gehrig: added GetDoubleValue()
- See list of many contributors in github
@section licence MIT LICENCE @section licence MIT LICENCE
The licence text below is the boilerplate "MIT Licence" used from: The licence text below is the boilerplate "MIT Licence" used from:
http://www.opensource.org/licenses/mit-license.php http://www.opensource.org/licenses/mit-license.php
Copyright (c) 2006-2012, Brodie Thiesfield Copyright (c) 2006-2024, Brodie Thiesfield
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@ -234,16 +250,16 @@
# define SI_ASSERT(x) # define SI_ASSERT(x)
#endif #endif
enum SI_Error { using SI_Error = int;
SI_OK = 0, //!< No error
SI_UPDATED = 1, //!< An existing value was updated
SI_INSERTED = 2, //!< A new value was inserted
// note: test for any error with (retval < 0) constexpr int SI_OK = 0; //!< No error
SI_FAIL = -1, //!< Generic failure constexpr int SI_UPDATED = 1; //!< An existing value was updated
SI_NOMEM = -2, //!< Out of memory error constexpr int SI_INSERTED = 2; //!< A new value was inserted
SI_FILE = -3 //!< File error (see errno for detail error)
}; // 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" #define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
@ -542,6 +558,35 @@ public:
/** Query the status of spaces output */ /** Query the status of spaces output */
bool UsingSpaces() const { return m_bSpaces; } 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; }
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
/** @} /** @}
@{ @name Loading INI Data */ @{ @name Loading INI Data */
@ -843,13 +888,27 @@ public:
are in use! are in use!
@param a_pSection Name of the section to return @param a_pSection Name of the section to return
@return boolean Was a section matching the supplied @return Section data
name found.
*/ */
const TKeyVal * GetSection( const TKeyVal * GetSection(
const SI_CHAR * a_pSection const SI_CHAR * a_pSection
) const; ) 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 /** Retrieve the value for a specific key. If multiple keys are enabled
(see SetMultiKey) then only the first value associated with that key (see SetMultiKey) then only the first value associated with that key
will be returned, see GetAllValues for getting all values with multikey. will be returned, see GetAllValues for getting all values with multikey.
@ -1219,6 +1278,7 @@ private:
bool IsMultiLineTag(const SI_CHAR * a_pData) const; bool IsMultiLineTag(const SI_CHAR * a_pData) const;
bool IsMultiLineData(const SI_CHAR * a_pData) const; bool IsMultiLineData(const SI_CHAR * a_pData) const;
bool IsSingleLineQuotedValue(const SI_CHAR* a_pData) const;
bool LoadMultiLineText( bool LoadMultiLineText(
SI_CHAR *& a_pData, SI_CHAR *& a_pData,
const SI_CHAR *& a_pVal, const SI_CHAR *& a_pVal,
@ -1250,6 +1310,9 @@ private:
/** File comment for this data, if one exists. */ /** File comment for this data, if one exists. */
const SI_CHAR * m_pFileComment; const SI_CHAR * m_pFileComment;
/** constant empty string */
const SI_CHAR m_cEmptyString;
/** Parsed INI data. Section -> (Key -> Value). */ /** Parsed INI data. Section -> (Key -> Value). */
TSection m_data; TSection m_data;
@ -1271,6 +1334,12 @@ private:
/** Should spaces be written out surrounding the equals sign? */ /** Should spaces be written out surrounding the equals sign? */
bool m_bSpaces; 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 /** Next order value, used to ensure sections and keys are output in the
same order that they are loaded/added. same order that they are loaded/added.
*/ */
@ -1290,10 +1359,13 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl(
: m_pData(0) : m_pData(0)
, m_uDataLen(0) , m_uDataLen(0)
, m_pFileComment(NULL) , m_pFileComment(NULL)
, m_cEmptyString(0)
, m_bStoreIsUtf8(a_bIsUtf8) , m_bStoreIsUtf8(a_bIsUtf8)
, m_bAllowMultiKey(a_bAllowMultiKey) , m_bAllowMultiKey(a_bAllowMultiKey)
, m_bAllowMultiLine(a_bAllowMultiLine) , m_bAllowMultiLine(a_bAllowMultiLine)
, m_bSpaces(true) , m_bSpaces(true)
, m_bParseQuotes(false)
, m_bAllowKeyOnly(false)
, m_nOrder(0) , m_nOrder(0)
{ } { }
@ -1392,7 +1464,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
} }
// allocate and ensure NULL terminated // allocate and ensure NULL terminated
char * pData = new(std::nothrow) char[lSize+1]; char * pData = new(std::nothrow) char[lSize+static_cast<size_t>(1)];
if (!pData) { if (!pData) {
return SI_NOMEM; return SI_NOMEM;
} }
@ -1550,6 +1622,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
{ {
a_pComment = NULL; a_pComment = NULL;
bool bHaveValue = false;
SI_CHAR * pTrail = NULL; SI_CHAR * pTrail = NULL;
while (*a_pData) { while (*a_pData) {
// skip spaces and empty lines // skip spaces and empty lines
@ -1607,19 +1680,20 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
} }
// find the end of the key name (it may contain spaces) // find the end of the key name (it may contain spaces)
// and convert it to lowercase as necessary
a_pKey = a_pData; a_pKey = a_pData;
while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) { while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
++a_pData; ++a_pData;
} }
// *a_pData is null, equals, or newline
// if it's an invalid line, just skip it // if no value and we don't allow no value, then invalid
if (*a_pData != '=') { bHaveValue = (*a_pData == '=');
if (!bHaveValue && !m_bAllowKeyOnly) {
continue; continue;
} }
// empty keys are invalid // empty keys are invalid
if (a_pKey == a_pData) { if (bHaveValue && a_pKey == a_pData) {
while (*a_pData && !IsNewLineChar(*a_pData)) { while (*a_pData && !IsNewLineChar(*a_pData)) {
++a_pData; ++a_pData;
} }
@ -1632,36 +1706,56 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
--pTrail; --pTrail;
} }
++pTrail; ++pTrail;
*pTrail = 0;
// skip leading whitespace on the value if (bHaveValue) {
++a_pData; // safe as checked that it == '=' above // process the value
while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) { *pTrail = 0;
++a_pData;
}
// find the end of the value which is the end of this line // skip leading whitespace on the value
a_pVal = a_pData; ++a_pData; // safe as checked that it == '=' above
while (*a_pData && !IsNewLineChar(*a_pData)) { while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
++a_pData; ++a_pData;
} }
// remove trailing spaces from the value // find the end of the value which is the end of this line
pTrail = a_pData - 1; a_pVal = a_pData;
if (*a_pData) { // prepare for the next round while (*a_pData && !IsNewLineChar(*a_pData)) {
SkipNewLine(a_pData); ++a_pData;
} }
while (pTrail >= a_pVal && IsSpace(*pTrail)) {
--pTrail;
}
++pTrail;
*pTrail = 0;
// check for multi-line entries // remove trailing spaces from the value
if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) { pTrail = a_pData - 1;
// skip the "<<<" to get the tag that will end the multiline if (*a_pData) { // prepare for the next round
const SI_CHAR * pTagName = a_pVal + 3; SkipNewLine(a_pData);
return LoadMultiLineText(a_pData, a_pVal, pTagName); }
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 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 // return the standard entry
@ -1721,6 +1815,41 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData(
return false; return false;
} }
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> template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
bool bool
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar( CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar(
@ -1754,8 +1883,8 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
a_pVal = a_pData; a_pVal = a_pData;
// find the end tag. This tag must start in column 1 and be // find the end tag. This tag must start in column 1 and be
// followed by a newline. No whitespace removal is done while // followed by a newline. We ignore any whitespace after the end
// searching for this tag. // tag but not whitespace before it.
SI_CHAR cEndOfLineChar = *a_pData; SI_CHAR cEndOfLineChar = *a_pData;
for(;;) { for(;;) {
// if we are loading comments then we need a comment character as // if we are loading comments then we need a comment character as
@ -1811,10 +1940,18 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
// if are looking for a tag then do the check now. This is done before // 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 // 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. // of the data then the tag is removed correctly.
if (a_pTagName && if (a_pTagName) {
(!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine))) // strip whitespace from the end of this tag
{ SI_CHAR* pc = a_pData - 1;
break; 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 // if we are at the end of the data then we just automatically end
@ -1916,7 +2053,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
// only set the comment if this is a section only entry // only set the comment if this is a section only entry
Entry oSection(a_pSection, ++m_nOrder); Entry oSection(a_pSection, ++m_nOrder);
if (a_pComment && (!a_pKey || !a_pValue)) { if (a_pComment && !a_pKey) {
oSection.pComment = a_pComment; oSection.pComment = a_pComment;
} }
@ -1926,14 +2063,15 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
iSection = i.first; iSection = i.first;
bInserted = true; bInserted = true;
} }
if (!a_pKey || !a_pValue) { if (!a_pKey) {
// section only entries are specified with pItem and pVal as NULL // section only entries are specified with pItem as NULL
return bInserted ? SI_INSERTED : SI_UPDATED; return bInserted ? SI_INSERTED : SI_UPDATED;
} }
// check for existence of the key // check for existence of the key
TKeyVal & keyval = iSection->second; TKeyVal & keyval = iSection->second;
typename TKeyVal::iterator iKey = keyval.find(a_pKey); typename TKeyVal::iterator iKey = keyval.find(a_pKey);
bInserted = iKey == keyval.end();
// remove all existing entries but save the load order and // remove all existing entries but save the load order and
// comment of the first entry // comment of the first entry
@ -1956,6 +2094,11 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
iKey = keyval.end(); 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 // make string copies if necessary
bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace; bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
if (a_bCopyStrings) { if (a_bCopyStrings) {
@ -1980,8 +2123,8 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
} }
typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL)); typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL));
iKey = keyval.insert(oEntry); iKey = keyval.insert(oEntry);
bInserted = true;
} }
iKey->second = a_pValue; iKey->second = a_pValue;
return bInserted ? SI_INSERTED : SI_UPDATED; return bInserted ? SI_INSERTED : SI_UPDATED;
} }
@ -2047,7 +2190,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetLongValue(
long nValue = a_nDefault; long nValue = a_nDefault;
char * pszSuffix = szValue; char * pszSuffix = szValue;
if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) { if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
if (!szValue[2]) return a_nDefault; if (!szValue[2]) return a_nDefault;
nValue = strtol(&szValue[2], &pszSuffix, 16); nValue = strtol(&szValue[2], &pszSuffix, 16);
} }
else { else {
@ -2078,7 +2221,11 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue(
// convert to an ASCII string // convert to an ASCII string
char szInput[64]; char szInput[64];
#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
#else // !__STDC_WANT_SECURE_LIB__
snprintf(szInput, sizeof(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 // convert to output text
SI_CHAR szOutput[64]; SI_CHAR szOutput[64];
@ -2124,28 +2271,32 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetDoubleValue(
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
SI_Error SI_Error
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetDoubleValue( CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetDoubleValue(
const SI_CHAR * a_pSection, const SI_CHAR * a_pSection,
const SI_CHAR * a_pKey, const SI_CHAR * a_pKey,
double a_nValue, double a_nValue,
const SI_CHAR * a_pComment, const SI_CHAR * a_pComment,
bool a_bForceReplace bool a_bForceReplace
) )
{ {
// use SetValue to create sections // use SetValue to create sections
if (!a_pSection || !a_pKey) return SI_FAIL; if (!a_pSection || !a_pKey) return SI_FAIL;
// convert to an ASCII string // convert to an ASCII string
char szInput[64]; char szInput[64];
snprintf(szInput, sizeof(szInput), "%f", a_nValue); #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
sprintf_s(szInput, "%f", a_nValue);
#else // !__STDC_WANT_SECURE_LIB__
snprintf(szInput, sizeof(szInput), "%f", a_nValue);
#endif // __STDC_WANT_SECURE_LIB__
// convert to output text // convert to output text
SI_CHAR szOutput[64]; SI_CHAR szOutput[64];
SI_CONVERTER c(m_bStoreIsUtf8); SI_CONVERTER c(m_bStoreIsUtf8);
c.ConvertFromStore(szInput, strlen(szInput) + 1, c.ConvertFromStore(szInput, strlen(szInput) + 1,
szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
// actually add it // actually add it
return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
} }
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
@ -2431,6 +2582,19 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
oSections.sort(typename Entry::LoadOrder()); oSections.sort(typename Entry::LoadOrder());
#endif #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 // write the file comment if we have one
bool bNeedNewLine = false; bool bNeedNewLine = false;
if (m_pFileComment) { if (m_pFileComment) {
@ -2506,22 +2670,31 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
} }
a_oOutput.Write(convert.Data()); a_oOutput.Write(convert.Data());
// write the value // write the value as long
if (!convert.ConvertToStore(iValue->pItem)) { if (*iValue->pItem || !m_bAllowKeyOnly) {
return SI_FAIL; if (!convert.ConvertToStore(iValue->pItem)) {
}
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)) {
return SI_FAIL; return SI_FAIL;
} }
a_oOutput.Write("END_OF_TEXT"); a_oOutput.Write(m_bSpaces ? " = " : "=");
} if (m_bParseQuotes && IsSingleLineQuotedValue(iValue->pItem)) {
else { // the only way to preserve external whitespace on a value (i.e. before or after)
a_oOutput.Write(convert.Data()); // 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); a_oOutput.Write(SI_NEWLINE_A);
} }
@ -2675,13 +2848,15 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString(
// SimpleIni.h, set the converter that you wish you use by defining one of the // SimpleIni.h, set the converter that you wish you use by defining one of the
// following symbols. // 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 // SI_CONVERT_GENERIC Use the Unicode reference conversion library in
// the accompanying files ConvertUTF.h/c // the accompanying files ConvertUTF.h/c
// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires // SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
// ICU headers on include path and icuuc.lib // ICU headers on include path and icuuc.lib
// SI_CONVERT_WIN32 Use the Win32 API functions for conversion. // 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 # ifdef _WIN32
# define SI_CONVERT_WIN32 # define SI_CONVERT_WIN32
# else # else
@ -2938,7 +3113,7 @@ public:
// This uses the Unicode reference implementation to do the // This uses the Unicode reference implementation to do the
// conversion from UTF-8 to wchar_t. The required files are // conversion from UTF-8 to wchar_t. The required files are
// ConvertUTF.h and ConvertUTF.c which should be included in // 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/ // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
ConversionResult retval; ConversionResult retval;
const UTF8 * pUtf8 = (const UTF8 *) a_pInputData; const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
@ -3025,7 +3200,7 @@ public:
// This uses the Unicode reference implementation to do the // This uses the Unicode reference implementation to do the
// conversion from wchar_t to UTF-8. The required files are // conversion from wchar_t to UTF-8. The required files are
// ConvertUTF.h and ConvertUTF.c which should be included in // 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/ // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
ConversionResult retval; ConversionResult retval;
UTF8 * pUtf8 = (UTF8 *) a_pOutputData; UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
@ -3275,13 +3450,8 @@ template<class SI_CHAR>
struct SI_NoCase { struct SI_NoCase {
bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
if (sizeof(SI_CHAR) == sizeof(char)) { if (sizeof(SI_CHAR) == sizeof(char)) {
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
return _mbsicmp((const unsigned char *)pLeft, return _mbsicmp((const unsigned char *)pLeft,
(const unsigned char *)pRight) < 0; (const unsigned char *)pRight) < 0;
#else
return _stricmp((const char*)pLeft,
(const char*)pRight) < 0;
#endif
} }
if (sizeof(SI_CHAR) == sizeof(wchar_t)) { if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
return _wcsicmp((const wchar_t *)pLeft, return _wcsicmp((const wchar_t *)pLeft,
@ -3416,6 +3586,19 @@ public:
#endif // SI_CONVERT_WIN32 #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 // TYPE DEFINITIONS
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -3425,27 +3608,35 @@ typedef CSimpleIniTempl<char,
typedef CSimpleIniTempl<char, typedef CSimpleIniTempl<char,
SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA; 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, typedef CSimpleIniTempl<UChar,
SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW; SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;
typedef CSimpleIniTempl<UChar, typedef CSimpleIniTempl<UChar,
SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW; SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;
#else # else
typedef CSimpleIniTempl<wchar_t, typedef CSimpleIniTempl<wchar_t,
SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW; SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;
typedef CSimpleIniTempl<wchar_t, typedef CSimpleIniTempl<wchar_t,
SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW; SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;
#endif # endif
#ifdef _UNICODE # ifdef _UNICODE
# define CSimpleIni CSimpleIniW # define CSimpleIni CSimpleIniW
# define CSimpleIniCase CSimpleIniCaseW # define CSimpleIniCase CSimpleIniCaseW
# define SI_NEWLINE SI_NEWLINE_W # define SI_NEWLINE SI_NEWLINE_W
#else // !_UNICODE # else // !_UNICODE
# define CSimpleIni CSimpleIniA # define CSimpleIni CSimpleIniA
# define CSimpleIniCase CSimpleIniCaseA # define CSimpleIniCase CSimpleIniCaseA
# define SI_NEWLINE SI_NEWLINE_A # define SI_NEWLINE SI_NEWLINE_A
#endif // _UNICODE # endif // _UNICODE
#endif
#ifdef _MSC_VER #ifdef _MSC_VER
# pragma warning (pop) # pragma warning (pop)

View File

@ -194,7 +194,7 @@ static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
* into the first byte, depending on how many bytes follow. There are * into the first byte, depending on how many bytes follow. There are
* as many entries in this table as there are UTF-8 sequence types. * as many entries in this table as there are UTF-8 sequence types.
* (I.e., one byte sequence, two byte... etc.). Remember that sequencs * (I.e., one byte sequence, two byte... etc.). Remember that sequences
* for *legal* UTF-8 will be 4 or fewer bytes total. * for *legal* UTF-8 will be 4 or fewer bytes total.
*/ */
static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };