From 6a5cb90b45891083fe4981f1d57d0ba5a6e37fc0 Mon Sep 17 00:00:00 2001 From: Brodie Thiesfield Date: Wed, 24 Jan 2007 18:48:17 +0000 Subject: version 4.1 release Fix compilation breakages on gcc. Changed multi-line entries so that regardless of platform the in memory value has lines separated by a single \n character and on save they are delimited by the platform newline characters. Added option to output the UTF-8 file signature to all Save and SaveFile methods. --- Makefile | 6 +- SimpleIni.h | 262 ++++++++++++++++++++++++++++++++++------------------- test1-expected.ini | 2 +- test1-input.ini | 2 +- test1.cpp | 22 +++-- testsi-UTF8.ini | 2 +- testsi.cpp | 12 +-- 7 files changed, 196 insertions(+), 112 deletions(-) diff --git a/Makefile b/Makefile index 04dbba6..c44c87b 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,12 @@ CPPFLAGS=-Wall OBJ=testsi.o test1.o snippets.o ConvertUTF.o -testsi: $(OBJ) +testsi: $(OBJ) gcc -o testsi -lstdc++ $(OBJ) clean: rm -f testsi $(OBJ) + +data: + sed 's/\r\n$$/\n/g' < test1-expected.ini > unix.out + mv unix.out test1-expected.ini diff --git a/SimpleIni.h b/SimpleIni.h index fdc5650..5b0f4de 100644 --- a/SimpleIni.h +++ b/SimpleIni.h @@ -5,7 +5,7 @@ File SimpleIni.h Author Brodie Thiesfield [code at jellycan dot com] Source http://code.jellycan.com/simpleini/ - Version 4.0.1 + Version 4.1 Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation. @@ -116,9 +116,10 @@ before ENDTAG in the end tag is not included in the data value. - The ending tag must be on it's own line with no whitespace before or after it. - - The multi-line value is not modified at load or save. This means - that the newline format (PC, Unix, Mac) is whatever the original - file uses. + - The multi-line value is modified at load so that each line in the value + is delimited by a single '\n' character on all platforms. At save time + it will be converted into the newline format used by the current + platform. @section COMMENTS @@ -233,7 +234,7 @@ enum SI_Error { SI_FILE = -3 //!< File error (see errno for detail error) }; -#define SI_BOM_UTF8 "\xEF\xBB\xBF" +#define SI_UTF8_SIGNATURE "\xEF\xBB\xBF" #ifdef _WIN32 # define SI_NEWLINE_A "\r\n" @@ -284,19 +285,18 @@ class CSimpleIniTempl { public: /** key entry */ - template - struct EntryTempl { + struct Entry { const SI_CHAR * pItem; const SI_CHAR * pComment; int nOrder; - EntryTempl(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0) + Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0) : pItem(a_pszItem) , pComment(NULL) , nOrder(a_nOrder) { } - EntryTempl(const EntryTempl & rhs) { operator=(rhs); } - EntryTempl & operator=(const EntryTempl & rhs) { + Entry(const Entry & rhs) { operator=(rhs); } + Entry & operator=(const Entry & rhs) { pItem = rhs.pItem; pComment = rhs.pComment; nOrder = rhs.nOrder; @@ -305,21 +305,21 @@ public: #if defined(_MSC_VER) && _MSC_VER <= 1200 /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */ - bool operator<(const EntryTempl & rhs) const { return LoadOrder()(*this, rhs); } - bool operator>(const EntryTempl & rhs) const { return LoadOrder()(rhs, *this); } + bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); } + bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); } #endif /** Strict less ordering by name of key only */ - struct KeyOrder : std::binary_function { - bool operator()(const EntryTempl & lhs, const EntryTempl & rhs) const { + struct KeyOrder : std::binary_function { + bool operator()(const Entry & lhs, const Entry & rhs) const { const static SI_STRLESS isLess = SI_STRLESS(); return isLess(lhs.pItem, rhs.pItem); } }; /** Strict less ordering by order, and then name of key */ - struct LoadOrder : std::binary_function { - bool operator()(const EntryTempl & lhs, const EntryTempl & rhs) const { + struct LoadOrder : std::binary_function { + bool operator()(const Entry & lhs, const Entry & rhs) const { if (lhs.nOrder != rhs.nOrder) { return lhs.nOrder < rhs.nOrder; } @@ -327,7 +327,6 @@ public: } }; }; - typedef EntryTempl Entry; /** map keys to values */ typedef std::multimap TKeyVal; @@ -397,14 +396,13 @@ public: /** Characterset conversion utility class to convert strings to the same format as is used for the storage. */ - template - class ConverterTempl : private SI_CONVERTER { + class Converter : private SI_CONVERTER { public: - ConverterTempl(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) { + Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) { m_scratch.resize(1024); } - ConverterTempl(const ConverterTempl & rhs) { operator=(rhs); } - ConverterTempl & operator=(const ConverterTempl & rhs) { + Converter(const Converter & rhs) { operator=(rhs); } + Converter & operator=(const Converter & rhs) { m_scratch = rhs.m_scratch; return *this; } @@ -425,7 +423,6 @@ public: private: std::string m_scratch; }; - typedef ConverterTempl Converter; public: /*-----------------------------------------------------------------------*/ @@ -592,10 +589,15 @@ public: to fopen() and so must be a valid path for the current platform. + @param a_bAddSignature Prepend the UTF-8 BOM if the output data is + in UTF-8 format. If it is not UTF-8 then + this parameter is ignored. + @return SI_Error See error definitions */ SI_Error SaveFile( - const char * a_pszFile + const char * a_pszFile, + bool a_bAddSignature = true ); #ifdef SI_HAS_WIDE_FILE @@ -603,26 +605,33 @@ public: @param a_pwszFile Path of the file to be saved in UTF-16. + @param a_bAddSignature Prepend the UTF-8 BOM if the output data is + in UTF-8 format. If it is not UTF-8 then + this parameter is ignored. + @return SI_Error See error definitions */ SI_Error SaveFile( - const SI_WCHAR_T * a_pwszFile + const SI_WCHAR_T * a_pwszFile, + bool a_bAddSignature = true ); #endif // _WIN32 - /** - * Save the INI data to a file. See Save() for details. Do not set - * a_bUseBOM to true if any information has been written to the file - * prior to calling this method. - * - * @param a_pFile Handle to a file. File should be opened for - * binary output. - * @param a_bUseBOM Prepend the UTF-8 BOM if the output data is - * in UTF-8 format. + /** Save the INI data to a file. See Save() for details. + + @param a_pFile Handle to a file. File should be opened for + binary output. + + @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in + UTF-8 format. If it is not UTF-8 then this value is + ignored. Do not set this to true if anything has + already been written to the file. + + @return SI_Error See error definitions */ SI_Error SaveFile( FILE * a_pFile, - bool a_bUseBOM = false + bool a_bAddSignature = false ) const; /** Save the INI data. The data will be written to the output device @@ -649,10 +658,16 @@ public: @param a_oOutput Output writer to write the data to. + @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in + UTF-8 format. If it is not UTF-8 then this value is + ignored. Do not set this to true if anything has + already been written to the OutputWriter. + @return SI_Error See error definitions */ SI_Error Save( - OutputWriter & a_oOutput + OutputWriter & a_oOutput, + bool a_bAddSignature = false ) const; #ifdef SI_SUPPORT_IOSTREAMS @@ -660,29 +675,41 @@ public: @param a_ostream String to have the INI data appended to. + @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in + UTF-8 format. If it is not UTF-8 then this value is + ignored. Do not set this to true if anything has + already been written to the stream. + @return SI_Error See error definitions */ SI_Error Save( - std::ostream & a_ostream + std::ostream & a_ostream, + bool a_bAddSignature = false ) const { StreamWriter writer(a_ostream); - return Save(writer); + return Save(writer, a_bAddSignature); } #endif // SI_SUPPORT_IOSTREAMS - /** Save the INI data to a string. See Save() for details. + /** Append the INI data to a string. See Save() for details. @param a_sBuffer String to have the INI data appended to. + @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in + UTF-8 format. If it is not UTF-8 then this value is + ignored. Do not set this to true if anything has + already been written to the string. + @return SI_Error See error definitions */ SI_Error Save( - std::string & a_sBuffer + std::string & a_sBuffer, + bool a_bAddSignature = false ) const { StringWriter writer(a_sBuffer); - return Save(writer); + return Save(writer, a_bAddSignature); } /*-----------------------------------------------------------------------*/ @@ -1044,9 +1071,9 @@ CSimpleIniTempl::LoadFile( { FILE * fp = NULL; #if __STDC_WANT_SECURE_LIB__ - fopen_s(&fp, a_pszFile, "rb"); + fopen_s(&fp, a_pszFile, "rb"); #else - fp = fopen(a_pszFile, "rb"); + fp = fopen(a_pszFile, "rb"); #endif if (!fp) { return SI_FILE; @@ -1066,9 +1093,9 @@ CSimpleIniTempl::LoadFile( #ifdef _WIN32 FILE * fp = NULL; #if __STDC_WANT_SECURE_LIB__ - _wfopen_s(&fp, a_pwszFile, L"rb"); + _wfopen_s(&fp, a_pwszFile, L"rb"); #else - fp = _wfopen(a_pwszFile, L"rb"); + fp = _wfopen(a_pwszFile, L"rb"); #endif if (!fp) return SI_FILE; SI_Error rc = LoadFile(fp); @@ -1125,7 +1152,7 @@ CSimpleIniTempl::Load( // consume the UTF-8 BOM if it exists if (m_bStoreIsUtf8 && a_uDataLen >= 3) { - if (memcmp(a_pData, SI_BOM_UTF8, 3) == 0) { + if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) { a_pData += 3; a_uDataLen -= 3; } @@ -1158,6 +1185,9 @@ CSimpleIniTempl::Load( const SI_CHAR * pItem = NULL; const SI_CHAR * pVal = NULL; const SI_CHAR * pComment = NULL; + + // We copy the strings if we are loading data into this class when we + // already have stored some. bool bCopyStrings = (m_pData != NULL); // find a file comment if it exists, this is a comment that starts at the @@ -1165,9 +1195,7 @@ CSimpleIniTempl::Load( SI_Error rc = FindFileComment(pWork, bCopyStrings); if (rc < 0) return rc; - // add every entry in the file to the data table. We copy the strings if - // we are loading data into this class when we already have stored some - // because we only store a single block. + // add every entry in the file to the data table while (FindEntry(pWork, pSection, pItem, pVal, pComment)) { rc = AddEntry(pSection, pItem, pVal, pComment, bCopyStrings); if (rc < 0) return rc; @@ -1459,49 +1487,74 @@ CSimpleIniTempl::FindMultiLine( const SI_CHAR *& a_pVal ) const { + // The multiline tag is passed into this function with the "<<::GetAllKeys( template SI_Error CSimpleIniTempl::SaveFile( - const char * a_pszFile + const char * a_pszFile, + bool a_bAddSignature ) { FILE * fp = NULL; #if __STDC_WANT_SECURE_LIB__ - fopen_s(&fp, a_pszFile, "wb"); + fopen_s(&fp, a_pszFile, "wb"); #else - fp = fopen(a_pszFile, "wb"); + fp = fopen(a_pszFile, "wb"); #endif if (!fp) return SI_FILE; - SI_Error rc = SaveFile(fp, true); + SI_Error rc = SaveFile(fp, a_bAddSignature); fclose(fp); return rc; } @@ -1798,19 +1852,20 @@ CSimpleIniTempl::SaveFile( template SI_Error CSimpleIniTempl::SaveFile( - const SI_WCHAR_T * a_pwszFile + const SI_WCHAR_T * a_pwszFile, + bool a_bAddSignature ) { #ifdef _WIN32 FILE * fp = _wfopen(a_pwszFile, L"wb"); if (!fp) return SI_FILE; - SI_Error rc = SaveFile(fp, true); + SI_Error rc = SaveFile(fp, a_bAddSignature); fclose(fp); return rc; #else // SI_CONVERT_ICU char szFile[256]; u_austrncpy(szFile, a_pwszFile, sizeof(szFile)); - return SaveFile(szFile); + return SaveFile(szFile, a_bAddSignature); #endif } #endif // SI_HAS_WIDE_FILE @@ -1819,24 +1874,27 @@ template SI_Error CSimpleIniTempl::SaveFile( FILE * a_pFile, - bool a_bUseBOM + bool a_bAddSignature ) const { FileWriter writer(a_pFile); - if (m_bStoreIsUtf8 && a_bUseBOM) { - writer.Write(SI_BOM_UTF8); - } - return Save(writer); + return Save(writer, a_bAddSignature); } template SI_Error CSimpleIniTempl::Save( - OutputWriter & a_oOutput + OutputWriter & a_oOutput, + bool a_bAddSignature ) const { Converter convert(m_bStoreIsUtf8); + // add the UTF-8 signature if it is desired + if (m_bStoreIsUtf8 && a_bAddSignature) { + a_oOutput.Write(SI_UTF8_SIGNATURE); + } + // get all of the sections sorted in load order TNamesDepend oSections; GetAllSections(oSections); @@ -1931,9 +1989,29 @@ CSimpleIniTempl::Save( } a_oOutput.Write("="); 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("<<pItem; + const SI_CHAR * pEndOfLine; + SI_CHAR cEndOfLineChar = *pLine; + while (cEndOfLineChar) { + // find the end of this line + pEndOfLine = pLine; + for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ; + cEndOfLineChar = *pEndOfLine; + + // temporarily null terminate, convert and output the line + *const_cast(pEndOfLine) = 0; + if (!convert.ConvertToStore(pLine)) { + return SI_FAIL; + } + *const_cast(pEndOfLine) = cEndOfLineChar; + pLine += (pEndOfLine - pLine) + 1; + a_oOutput.Write(convert.Data()); + a_oOutput.Write(SI_NEWLINE_A); + } + a_oOutput.Write("SI-END-OF-MULTILINE-TEXT"); } else { a_oOutput.Write(convert.Data()); @@ -2058,7 +2136,7 @@ CSimpleIniTempl::DeleteString( */ template struct SI_GenericCase { - bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { + bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { long cmp; for ( ;*pLeft && *pRight; ++pLeft, ++pRight) { cmp = (long) *pLeft - (long) *pRight; @@ -2081,7 +2159,7 @@ struct SI_GenericNoCase { inline SI_CHAR locase(SI_CHAR ch) const { return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a'); } - bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { + bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { long cmp; for ( ;*pLeft && *pRight; ++pLeft, ++pRight) { cmp = (long) locase(*pLeft) - (long) locase(*pRight); @@ -2632,7 +2710,7 @@ public: #include template 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)) { return _mbsicmp((const unsigned char *)pLeft, (const unsigned char *)pRight) < 0; diff --git a/test1-expected.ini b/test1-expected.ini index 5b4645a..04fcf65 100644 --- a/test1-expected.ini +++ b/test1-expected.ini @@ -1,4 +1,4 @@ -; testsi-UTF8-std.ini : standard UTF-8 test file for SimpleIni automated testing +; testsi-UTF8-std.ini : standard UTF-8 test file for SimpleIni automated testing ; ; The number after a section or key is the order that it is defined in this file ; to make it easier to see if it has been written out correctly. This file should diff --git a/test1-input.ini b/test1-input.ini index a79f06b..5482db5 100644 --- a/test1-input.ini +++ b/test1-input.ini @@ -1,4 +1,4 @@ -; testsi-UTF8-std.ini : standard UTF-8 test file for SimpleIni automated testing +; testsi-UTF8-std.ini : standard UTF-8 test file for SimpleIni automated testing ; ; The number after a section or key is the order that it is defined in this file ; to make it easier to see if it has been written out correctly. This file should diff --git a/test1.cpp b/test1.cpp index 5d92ea7..4e11d19 100644 --- a/test1.cpp +++ b/test1.cpp @@ -9,8 +9,11 @@ # pragma warning(disable: 4786) #endif -#ifndef _WIN32 +#ifdef _WIN32 +# define DELETE_FILE DeleteFileA +#else # include +# define DELETE_FILE unlink #endif #include @@ -22,19 +25,19 @@ class Test std::string m_strTest; public: - Test(const char * a_pszName) - : m_strTest(a_pszName) + Test(const char * a_pszName) + : m_strTest(a_pszName) { printf("%s: test starting\n", m_strTest.c_str()); } - bool Success() + bool Success() { printf("%s: test succeeded\n", m_strTest.c_str()); return false; } - bool Failure(const char * pszReason) + bool Failure(const char * pszReason) { printf("%s: test FAILED (%s)\n", m_strTest.c_str(), pszReason); return false; @@ -48,7 +51,7 @@ bool FileComparisonTest(const char * a_pszFile1, const char * a_pszFile2) { char szBuf[1024]; FILE * fp = NULL; - + #if __STDC_WANT_SECURE_LIB__ fopen_s(&fp, a_pszFile1, "rb"); #else @@ -97,9 +100,8 @@ bool FileLoadTest(const char * a_pszFile1, const char * a_pszFile2) { if (ini.SaveFile("test2.ini") < 0) throw "Save failed for file 2"; b = FileComparisonTest("test1.ini", "test2.ini"); - _unlink("test1.ini"); - _unlink("test2.ini"); - + DELETE_FILE("test1.ini"); + DELETE_FILE("test2.ini"); if (!b) throw "File comparison failed in FileLoadTest"; } catch (...) { @@ -144,7 +146,7 @@ bool TestStreams() try { std::ofstream outfile; outfile.open(rgszTestFile[1], std::ofstream::out | std::ofstream::binary); - if (ini.Save(outfile) < 0) throw false; + if (ini.Save(outfile, true) < 0) throw false; outfile.close(); } catch (...) { diff --git a/testsi-UTF8.ini b/testsi-UTF8.ini index c010f4e..37d8e6d 100644 --- a/testsi-UTF8.ini +++ b/testsi-UTF8.ini @@ -1,4 +1,4 @@ -; test file for SimpleIni +; test file for SimpleIni whitespace = ok nosection=ok diff --git a/testsi.cpp b/testsi.cpp index 8bd5ded..b258a66 100644 --- a/testsi.cpp +++ b/testsi.cpp @@ -196,18 +196,18 @@ TestFile( fp = fopen("testsi-out-comment.ini", "wb"); #endif if (fp) { + CSimpleIni::FileWriter writer(fp); + if (a_bIsUtf8) { + writer.Write(SI_UTF8_SIGNATURE); + } + // add a string to the file in the correct text format CSimpleIni::Converter convert = ini.GetConverter(); convert.ConvertToStore(_T("; output from testsi.cpp test program") SI_NEWLINE SI_NEWLINE); - - CSimpleIni::FileWriter writer(fp); - if (a_bIsUtf8) { - writer.Write(SI_BOM_UTF8); - } writer.Write(convert.Data()); - ini.Save(writer); + ini.Save(writer, false); fclose(fp); } -- cgit v1.2.3