|
SimpleIni
|
00001 00194 #ifndef INCLUDED_SimpleIni_h 00195 #define INCLUDED_SimpleIni_h 00196 00197 #if defined(_MSC_VER) && (_MSC_VER >= 1020) 00198 # pragma once 00199 #endif 00200 00201 // Disable these warnings in MSVC: 00202 // 4127 "conditional expression is constant" as the conversion classes trigger 00203 // it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will 00204 // be optimized away in a release build. 00205 // 4503 'insert' : decorated name length exceeded, name was truncated 00206 // 4702 "unreachable code" as the MS STL header causes it in release mode. 00207 // Again, the code causing the warning will be cleaned up by the compiler. 00208 // 4786 "identifier truncated to 256 characters" as this is thrown hundreds 00209 // of times VC6 as soon as STL is used. 00210 #ifdef _MSC_VER 00211 # pragma warning (push) 00212 # pragma warning (disable: 4127 4503 4702 4786) 00213 #endif 00214 00215 #include <cstring> 00216 #include <string> 00217 #include <map> 00218 #include <list> 00219 #include <algorithm> 00220 #include <stdio.h> 00221 00222 #ifdef SI_SUPPORT_IOSTREAMS 00223 # include <iostream> 00224 #endif // SI_SUPPORT_IOSTREAMS 00225 00226 #ifdef _DEBUG 00227 # ifndef assert 00228 # include <cassert> 00229 # endif 00230 # define SI_ASSERT(x) assert(x) 00231 #else 00232 # define SI_ASSERT(x) 00233 #endif 00234 00235 enum SI_Error { 00236 SI_OK = 0, 00237 SI_UPDATED = 1, 00238 SI_INSERTED = 2, 00239 00240 // note: test for any error with (retval < 0) 00241 SI_FAIL = -1, 00242 SI_NOMEM = -2, 00243 SI_FILE = -3 00244 }; 00245 00246 #define SI_UTF8_SIGNATURE "\xEF\xBB\xBF" 00247 00248 #ifdef _WIN32 00249 # define SI_NEWLINE_A "\r\n" 00250 # define SI_NEWLINE_W L"\r\n" 00251 #else // !_WIN32 00252 # define SI_NEWLINE_A "\n" 00253 # define SI_NEWLINE_W L"\n" 00254 #endif // _WIN32 00255 00256 #if defined(SI_CONVERT_ICU) 00257 # include <unicode/ustring.h> 00258 #endif 00259 00260 #if defined(_WIN32) 00261 # define SI_HAS_WIDE_FILE 00262 # define SI_WCHAR_T wchar_t 00263 #elif defined(SI_CONVERT_ICU) 00264 # define SI_HAS_WIDE_FILE 00265 # define SI_WCHAR_T UChar 00266 #endif 00267 00268 00269 // --------------------------------------------------------------------------- 00270 // MAIN TEMPLATE CLASS 00271 // --------------------------------------------------------------------------- 00272 00292 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 00293 class CSimpleIniTempl 00294 { 00295 public: 00297 struct Entry { 00298 const SI_CHAR * pItem; 00299 const SI_CHAR * pComment; 00300 int nOrder; 00301 00302 Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0) 00303 : pItem(a_pszItem) 00304 , pComment(NULL) 00305 , nOrder(a_nOrder) 00306 { } 00307 Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder) 00308 : pItem(a_pszItem) 00309 , pComment(a_pszComment) 00310 , nOrder(a_nOrder) 00311 { } 00312 Entry(const Entry & rhs) { operator=(rhs); } 00313 Entry & operator=(const Entry & rhs) { 00314 pItem = rhs.pItem; 00315 pComment = rhs.pComment; 00316 nOrder = rhs.nOrder; 00317 return *this; 00318 } 00319 00320 #if defined(_MSC_VER) && _MSC_VER <= 1200 00321 00322 bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); } 00323 bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); } 00324 #endif 00325 00327 struct KeyOrder : std::binary_function<Entry, Entry, bool> { 00328 bool operator()(const Entry & lhs, const Entry & rhs) const { 00329 const static SI_STRLESS isLess = SI_STRLESS(); 00330 return isLess(lhs.pItem, rhs.pItem); 00331 } 00332 }; 00333 00335 struct LoadOrder : std::binary_function<Entry, Entry, bool> { 00336 bool operator()(const Entry & lhs, const Entry & rhs) const { 00337 if (lhs.nOrder != rhs.nOrder) { 00338 return lhs.nOrder < rhs.nOrder; 00339 } 00340 return KeyOrder()(lhs.pItem, rhs.pItem); 00341 } 00342 }; 00343 }; 00344 00346 typedef std::multimap<Entry,const SI_CHAR *,typename Entry::KeyOrder> TKeyVal; 00347 00349 typedef std::map<Entry,TKeyVal,typename Entry::KeyOrder> TSection; 00350 00354 typedef std::list<Entry> TNamesDepend; 00355 00359 class OutputWriter { 00360 public: 00361 OutputWriter() { } 00362 virtual ~OutputWriter() { } 00363 virtual void Write(const char * a_pBuf) = 0; 00364 private: 00365 OutputWriter(const OutputWriter &); // disable 00366 OutputWriter & operator=(const OutputWriter &); // disable 00367 }; 00368 00370 class FileWriter : public OutputWriter { 00371 FILE * m_file; 00372 public: 00373 FileWriter(FILE * a_file) : m_file(a_file) { } 00374 void Write(const char * a_pBuf) { 00375 fputs(a_pBuf, m_file); 00376 } 00377 private: 00378 FileWriter(const FileWriter &); // disable 00379 FileWriter & operator=(const FileWriter &); // disable 00380 }; 00381 00383 class StringWriter : public OutputWriter { 00384 std::string & m_string; 00385 public: 00386 StringWriter(std::string & a_string) : m_string(a_string) { } 00387 void Write(const char * a_pBuf) { 00388 m_string.append(a_pBuf); 00389 } 00390 private: 00391 StringWriter(const StringWriter &); // disable 00392 StringWriter & operator=(const StringWriter &); // disable 00393 }; 00394 00395 #ifdef SI_SUPPORT_IOSTREAMS 00396 00397 class StreamWriter : public OutputWriter { 00398 std::ostream & m_ostream; 00399 public: 00400 StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { } 00401 void Write(const char * a_pBuf) { 00402 m_ostream << a_pBuf; 00403 } 00404 private: 00405 StreamWriter(const StreamWriter &); // disable 00406 StreamWriter & operator=(const StreamWriter &); // disable 00407 }; 00408 #endif // SI_SUPPORT_IOSTREAMS 00409 00413 class Converter : private SI_CONVERTER { 00414 public: 00415 Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) { 00416 m_scratch.resize(1024); 00417 } 00418 Converter(const Converter & rhs) { operator=(rhs); } 00419 Converter & operator=(const Converter & rhs) { 00420 m_scratch = rhs.m_scratch; 00421 return *this; 00422 } 00423 bool ConvertToStore(const SI_CHAR * a_pszString) { 00424 size_t uLen = SizeToStore(a_pszString); 00425 if (uLen == (size_t)(-1)) { 00426 return false; 00427 } 00428 while (uLen > m_scratch.size()) { 00429 m_scratch.resize(m_scratch.size() * 2); 00430 } 00431 return SI_CONVERTER::ConvertToStore( 00432 a_pszString, 00433 const_cast<char*>(m_scratch.data()), 00434 m_scratch.size()); 00435 } 00436 const char * Data() { return m_scratch.data(); } 00437 private: 00438 std::string m_scratch; 00439 }; 00440 00441 public: 00442 /*-----------------------------------------------------------------------*/ 00443 00450 CSimpleIniTempl( 00451 bool a_bIsUtf8 = false, 00452 bool a_bMultiKey = false, 00453 bool a_bMultiLine = false 00454 ); 00455 00457 ~CSimpleIniTempl(); 00458 00460 void Reset(); 00461 00463 bool IsEmpty() const { return m_data.empty(); } 00464 00465 /*-----------------------------------------------------------------------*/ 00482 void SetUnicode(bool a_bIsUtf8 = true) { 00483 if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8; 00484 } 00485 00487 bool IsUnicode() const { return m_bStoreIsUtf8; } 00488 00507 void SetMultiKey(bool a_bAllowMultiKey = true) { 00508 m_bAllowMultiKey = a_bAllowMultiKey; 00509 } 00510 00512 bool IsMultiKey() const { return m_bAllowMultiKey; } 00513 00521 void SetMultiLine(bool a_bAllowMultiLine = true) { 00522 m_bAllowMultiLine = a_bAllowMultiLine; 00523 } 00524 00526 bool IsMultiLine() const { return m_bAllowMultiLine; } 00527 00534 void SetSpaces(bool a_bSpaces = true) { 00535 m_bSpaces = a_bSpaces; 00536 } 00537 00539 bool UsingSpaces() const { return m_bSpaces; } 00540 00541 /*-----------------------------------------------------------------------*/ 00553 SI_Error LoadFile( 00554 const char * a_pszFile 00555 ); 00556 00557 #ifdef SI_HAS_WIDE_FILE 00558 00564 SI_Error LoadFile( 00565 const SI_WCHAR_T * a_pwszFile 00566 ); 00567 #endif // SI_HAS_WIDE_FILE 00568 00576 SI_Error LoadFile( 00577 FILE * a_fpFile 00578 ); 00579 00580 #ifdef SI_SUPPORT_IOSTREAMS 00581 00587 SI_Error LoadData( 00588 std::istream & a_istream 00589 ); 00590 #endif // SI_SUPPORT_IOSTREAMS 00591 00598 SI_Error LoadData(const std::string & a_strData) { 00599 return LoadData(a_strData.c_str(), a_strData.size()); 00600 } 00601 00609 SI_Error LoadData( 00610 const char * a_pData, 00611 size_t a_uDataLen 00612 ); 00613 00614 /*-----------------------------------------------------------------------*/ 00630 SI_Error SaveFile( 00631 const char * a_pszFile, 00632 bool a_bAddSignature = true 00633 ) const; 00634 00635 #ifdef SI_HAS_WIDE_FILE 00636 00646 SI_Error SaveFile( 00647 const SI_WCHAR_T * a_pwszFile, 00648 bool a_bAddSignature = true 00649 ) const; 00650 #endif // _WIN32 00651 00664 SI_Error SaveFile( 00665 FILE * a_pFile, 00666 bool a_bAddSignature = false 00667 ) const; 00668 00700 SI_Error Save( 00701 OutputWriter & a_oOutput, 00702 bool a_bAddSignature = false 00703 ) const; 00704 00705 #ifdef SI_SUPPORT_IOSTREAMS 00706 00717 SI_Error Save( 00718 std::ostream & a_ostream, 00719 bool a_bAddSignature = false 00720 ) const 00721 { 00722 StreamWriter writer(a_ostream); 00723 return Save(writer, a_bAddSignature); 00724 } 00725 #endif // SI_SUPPORT_IOSTREAMS 00726 00738 SI_Error Save( 00739 std::string & a_sBuffer, 00740 bool a_bAddSignature = false 00741 ) const 00742 { 00743 StringWriter writer(a_sBuffer); 00744 return Save(writer, a_bAddSignature); 00745 } 00746 00747 /*-----------------------------------------------------------------------*/ 00765 void GetAllSections( 00766 TNamesDepend & a_names 00767 ) const; 00768 00786 bool GetAllKeys( 00787 const SI_CHAR * a_pSection, 00788 TNamesDepend & a_names 00789 ) const; 00790 00807 bool GetAllValues( 00808 const SI_CHAR * a_pSection, 00809 const SI_CHAR * a_pKey, 00810 TNamesDepend & a_values 00811 ) const; 00812 00822 int GetSectionSize( 00823 const SI_CHAR * a_pSection 00824 ) const; 00825 00840 const TKeyVal * GetSection( 00841 const SI_CHAR * a_pSection 00842 ) const; 00843 00861 const SI_CHAR * GetValue( 00862 const SI_CHAR * a_pSection, 00863 const SI_CHAR * a_pKey, 00864 const SI_CHAR * a_pDefault = NULL, 00865 bool * a_pHasMultiple = NULL 00866 ) const; 00867 00881 long GetLongValue( 00882 const SI_CHAR * a_pSection, 00883 const SI_CHAR * a_pKey, 00884 long a_nDefault = 0, 00885 bool * a_pHasMultiple = NULL 00886 ) const; 00887 00901 double GetDoubleValue( 00902 const SI_CHAR * a_pSection, 00903 const SI_CHAR * a_pKey, 00904 double a_nDefault = 0, 00905 bool * a_pHasMultiple = NULL 00906 ) const; 00907 00926 bool GetBoolValue( 00927 const SI_CHAR * a_pSection, 00928 const SI_CHAR * a_pKey, 00929 bool a_bDefault = false, 00930 bool * a_pHasMultiple = NULL 00931 ) const; 00932 00962 SI_Error SetValue( 00963 const SI_CHAR * a_pSection, 00964 const SI_CHAR * a_pKey, 00965 const SI_CHAR * a_pValue, 00966 const SI_CHAR * a_pComment = NULL, 00967 bool a_bForceReplace = false 00968 ) 00969 { 00970 return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true); 00971 } 00972 00996 SI_Error SetLongValue( 00997 const SI_CHAR * a_pSection, 00998 const SI_CHAR * a_pKey, 00999 long a_nValue, 01000 const SI_CHAR * a_pComment = NULL, 01001 bool a_bUseHex = false, 01002 bool a_bForceReplace = false 01003 ); 01004 01025 SI_Error SetDoubleValue( 01026 const SI_CHAR * a_pSection, 01027 const SI_CHAR * a_pKey, 01028 double a_nValue, 01029 const SI_CHAR * a_pComment = NULL, 01030 bool a_bForceReplace = false 01031 ); 01032 01053 SI_Error SetBoolValue( 01054 const SI_CHAR * a_pSection, 01055 const SI_CHAR * a_pKey, 01056 bool a_bValue, 01057 const SI_CHAR * a_pComment = NULL, 01058 bool a_bForceReplace = false 01059 ); 01060 01079 bool Delete( 01080 const SI_CHAR * a_pSection, 01081 const SI_CHAR * a_pKey, 01082 bool a_bRemoveEmpty = false 01083 ); 01084 01085 /*-----------------------------------------------------------------------*/ 01094 Converter GetConverter() const { 01095 return Converter(m_bStoreIsUtf8); 01096 } 01097 01098 /*-----------------------------------------------------------------------*/ 01101 private: 01102 // copying is not permitted 01103 CSimpleIniTempl(const CSimpleIniTempl &); // disabled 01104 CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled 01105 01108 SI_Error FindFileComment( 01109 SI_CHAR *& a_pData, 01110 bool a_bCopyStrings 01111 ); 01112 01117 bool FindEntry( 01118 SI_CHAR *& a_pData, 01119 const SI_CHAR *& a_pSection, 01120 const SI_CHAR *& a_pKey, 01121 const SI_CHAR *& a_pVal, 01122 const SI_CHAR *& a_pComment 01123 ) const; 01124 01147 SI_Error AddEntry( 01148 const SI_CHAR * a_pSection, 01149 const SI_CHAR * a_pKey, 01150 const SI_CHAR * a_pValue, 01151 const SI_CHAR * a_pComment, 01152 bool a_bForceReplace, 01153 bool a_bCopyStrings 01154 ); 01155 01157 inline bool IsSpace(SI_CHAR ch) const { 01158 return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); 01159 } 01160 01162 inline bool IsComment(SI_CHAR ch) const { 01163 return (ch == ';' || ch == '#'); 01164 } 01165 01166 01168 inline void SkipNewLine(SI_CHAR *& a_pData) const { 01169 a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1; 01170 } 01171 01173 SI_Error CopyString(const SI_CHAR *& a_pString); 01174 01176 void DeleteString(const SI_CHAR * a_pString); 01177 01179 bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const { 01180 const static SI_STRLESS isLess = SI_STRLESS(); 01181 return isLess(a_pLeft, a_pRight); 01182 } 01183 01184 bool IsMultiLineTag(const SI_CHAR * a_pData) const; 01185 bool IsMultiLineData(const SI_CHAR * a_pData) const; 01186 bool LoadMultiLineText( 01187 SI_CHAR *& a_pData, 01188 const SI_CHAR *& a_pVal, 01189 const SI_CHAR * a_pTagName, 01190 bool a_bAllowBlankLinesInComment = false 01191 ) const; 01192 bool IsNewLineChar(SI_CHAR a_c) const; 01193 01194 bool OutputMultiLineText( 01195 OutputWriter & a_oOutput, 01196 Converter & a_oConverter, 01197 const SI_CHAR * a_pText 01198 ) const; 01199 01200 private: 01206 SI_CHAR * m_pData; 01207 01212 size_t m_uDataLen; 01213 01215 const SI_CHAR * m_pFileComment; 01216 01218 TSection m_data; 01219 01224 TNamesDepend m_strings; 01225 01227 bool m_bStoreIsUtf8; 01228 01230 bool m_bAllowMultiKey; 01231 01233 bool m_bAllowMultiLine; 01234 01236 bool m_bSpaces; 01237 01241 int m_nOrder; 01242 }; 01243 01244 // --------------------------------------------------------------------------- 01245 // IMPLEMENTATION 01246 // --------------------------------------------------------------------------- 01247 01248 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01249 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl( 01250 bool a_bIsUtf8, 01251 bool a_bAllowMultiKey, 01252 bool a_bAllowMultiLine 01253 ) 01254 : m_pData(0) 01255 , m_uDataLen(0) 01256 , m_pFileComment(NULL) 01257 , m_bStoreIsUtf8(a_bIsUtf8) 01258 , m_bAllowMultiKey(a_bAllowMultiKey) 01259 , m_bAllowMultiLine(a_bAllowMultiLine) 01260 , m_bSpaces(true) 01261 , m_nOrder(0) 01262 { } 01263 01264 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01265 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::~CSimpleIniTempl() 01266 { 01267 Reset(); 01268 } 01269 01270 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01271 void 01272 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Reset() 01273 { 01274 // remove all data 01275 delete[] m_pData; 01276 m_pData = NULL; 01277 m_uDataLen = 0; 01278 m_pFileComment = NULL; 01279 if (!m_data.empty()) { 01280 m_data.erase(m_data.begin(), m_data.end()); 01281 } 01282 01283 // remove all strings 01284 if (!m_strings.empty()) { 01285 typename TNamesDepend::iterator i = m_strings.begin(); 01286 for (; i != m_strings.end(); ++i) { 01287 delete[] const_cast<SI_CHAR*>(i->pItem); 01288 } 01289 m_strings.erase(m_strings.begin(), m_strings.end()); 01290 } 01291 } 01292 01293 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01294 SI_Error 01295 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile( 01296 const char * a_pszFile 01297 ) 01298 { 01299 FILE * fp = NULL; 01300 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE 01301 fopen_s(&fp, a_pszFile, "rb"); 01302 #else // !__STDC_WANT_SECURE_LIB__ 01303 fp = fopen(a_pszFile, "rb"); 01304 #endif // __STDC_WANT_SECURE_LIB__ 01305 if (!fp) { 01306 return SI_FILE; 01307 } 01308 SI_Error rc = LoadFile(fp); 01309 fclose(fp); 01310 return rc; 01311 } 01312 01313 #ifdef SI_HAS_WIDE_FILE 01314 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01315 SI_Error 01316 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile( 01317 const SI_WCHAR_T * a_pwszFile 01318 ) 01319 { 01320 #ifdef _WIN32 01321 FILE * fp = NULL; 01322 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE 01323 _wfopen_s(&fp, a_pwszFile, L"rb"); 01324 #else // !__STDC_WANT_SECURE_LIB__ 01325 fp = _wfopen(a_pwszFile, L"rb"); 01326 #endif // __STDC_WANT_SECURE_LIB__ 01327 if (!fp) return SI_FILE; 01328 SI_Error rc = LoadFile(fp); 01329 fclose(fp); 01330 return rc; 01331 #else // !_WIN32 (therefore SI_CONVERT_ICU) 01332 char szFile[256]; 01333 u_austrncpy(szFile, a_pwszFile, sizeof(szFile)); 01334 return LoadFile(szFile); 01335 #endif // _WIN32 01336 } 01337 #endif // SI_HAS_WIDE_FILE 01338 01339 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01340 SI_Error 01341 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile( 01342 FILE * a_fpFile 01343 ) 01344 { 01345 // load the raw file data 01346 int retval = fseek(a_fpFile, 0, SEEK_END); 01347 if (retval != 0) { 01348 return SI_FILE; 01349 } 01350 long lSize = ftell(a_fpFile); 01351 if (lSize < 0) { 01352 return SI_FILE; 01353 } 01354 if (lSize == 0) { 01355 return SI_OK; 01356 } 01357 char * pData = new char[lSize]; 01358 if (!pData) { 01359 return SI_NOMEM; 01360 } 01361 fseek(a_fpFile, 0, SEEK_SET); 01362 size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile); 01363 if (uRead != (size_t) lSize) { 01364 delete[] pData; 01365 return SI_FILE; 01366 } 01367 01368 // convert the raw data to unicode 01369 SI_Error rc = LoadData(pData, uRead); 01370 delete[] pData; 01371 return rc; 01372 } 01373 01374 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01375 SI_Error 01376 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData( 01377 const char * a_pData, 01378 size_t a_uDataLen 01379 ) 01380 { 01381 SI_CONVERTER converter(m_bStoreIsUtf8); 01382 01383 if (a_uDataLen == 0) { 01384 return SI_OK; 01385 } 01386 01387 // consume the UTF-8 BOM if it exists 01388 if (m_bStoreIsUtf8 && a_uDataLen >= 3) { 01389 if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) { 01390 a_pData += 3; 01391 a_uDataLen -= 3; 01392 } 01393 } 01394 01395 // determine the length of the converted data 01396 size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen); 01397 if (uLen == (size_t)(-1)) { 01398 return SI_FAIL; 01399 } 01400 01401 // allocate memory for the data, ensure that there is a NULL 01402 // terminator wherever the converted data ends 01403 SI_CHAR * pData = new SI_CHAR[uLen+1]; 01404 if (!pData) { 01405 return SI_NOMEM; 01406 } 01407 memset(pData, 0, sizeof(SI_CHAR)*(uLen+1)); 01408 01409 // convert the data 01410 if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) { 01411 delete[] pData; 01412 return SI_FAIL; 01413 } 01414 01415 // parse it 01416 const static SI_CHAR empty = 0; 01417 SI_CHAR * pWork = pData; 01418 const SI_CHAR * pSection = ∅ 01419 const SI_CHAR * pItem = NULL; 01420 const SI_CHAR * pVal = NULL; 01421 const SI_CHAR * pComment = NULL; 01422 01423 // We copy the strings if we are loading data into this class when we 01424 // already have stored some. 01425 bool bCopyStrings = (m_pData != NULL); 01426 01427 // find a file comment if it exists, this is a comment that starts at the 01428 // beginning of the file and continues until the first blank line. 01429 SI_Error rc = FindFileComment(pWork, bCopyStrings); 01430 if (rc < 0) return rc; 01431 01432 // add every entry in the file to the data table 01433 while (FindEntry(pWork, pSection, pItem, pVal, pComment)) { 01434 rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings); 01435 if (rc < 0) return rc; 01436 } 01437 01438 // store these strings if we didn't copy them 01439 if (bCopyStrings) { 01440 delete[] pData; 01441 } 01442 else { 01443 m_pData = pData; 01444 m_uDataLen = uLen+1; 01445 } 01446 01447 return SI_OK; 01448 } 01449 01450 #ifdef SI_SUPPORT_IOSTREAMS 01451 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01452 SI_Error 01453 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData( 01454 std::istream & a_istream 01455 ) 01456 { 01457 std::string strData; 01458 char szBuf[512]; 01459 do { 01460 a_istream.get(szBuf, sizeof(szBuf), '\0'); 01461 strData.append(szBuf); 01462 } 01463 while (a_istream.good()); 01464 return LoadData(strData); 01465 } 01466 #endif // SI_SUPPORT_IOSTREAMS 01467 01468 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01469 SI_Error 01470 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindFileComment( 01471 SI_CHAR *& a_pData, 01472 bool a_bCopyStrings 01473 ) 01474 { 01475 // there can only be a single file comment 01476 if (m_pFileComment) { 01477 return SI_OK; 01478 } 01479 01480 // Load the file comment as multi-line text, this will modify all of 01481 // the newline characters to be single \n chars 01482 if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) { 01483 return SI_OK; 01484 } 01485 01486 // copy the string if necessary 01487 if (a_bCopyStrings) { 01488 SI_Error rc = CopyString(m_pFileComment); 01489 if (rc < 0) return rc; 01490 } 01491 01492 return SI_OK; 01493 } 01494 01495 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01496 bool 01497 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry( 01498 SI_CHAR *& a_pData, 01499 const SI_CHAR *& a_pSection, 01500 const SI_CHAR *& a_pKey, 01501 const SI_CHAR *& a_pVal, 01502 const SI_CHAR *& a_pComment 01503 ) const 01504 { 01505 a_pComment = NULL; 01506 01507 SI_CHAR * pTrail = NULL; 01508 while (*a_pData) { 01509 // skip spaces and empty lines 01510 while (*a_pData && IsSpace(*a_pData)) { 01511 ++a_pData; 01512 } 01513 if (!*a_pData) { 01514 break; 01515 } 01516 01517 // skip processing of comment lines but keep a pointer to 01518 // the start of the comment. 01519 if (IsComment(*a_pData)) { 01520 LoadMultiLineText(a_pData, a_pComment, NULL, true); 01521 continue; 01522 } 01523 01524 // process section names 01525 if (*a_pData == '[') { 01526 // skip leading spaces 01527 ++a_pData; 01528 while (*a_pData && IsSpace(*a_pData)) { 01529 ++a_pData; 01530 } 01531 01532 // find the end of the section name (it may contain spaces) 01533 // and convert it to lowercase as necessary 01534 a_pSection = a_pData; 01535 while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) { 01536 ++a_pData; 01537 } 01538 01539 // if it's an invalid line, just skip it 01540 if (*a_pData != ']') { 01541 continue; 01542 } 01543 01544 // remove trailing spaces from the section 01545 pTrail = a_pData - 1; 01546 while (pTrail >= a_pSection && IsSpace(*pTrail)) { 01547 --pTrail; 01548 } 01549 ++pTrail; 01550 *pTrail = 0; 01551 01552 // skip to the end of the line 01553 ++a_pData; // safe as checked that it == ']' above 01554 while (*a_pData && !IsNewLineChar(*a_pData)) { 01555 ++a_pData; 01556 } 01557 01558 a_pKey = NULL; 01559 a_pVal = NULL; 01560 return true; 01561 } 01562 01563 // find the end of the key name (it may contain spaces) 01564 // and convert it to lowercase as necessary 01565 a_pKey = a_pData; 01566 while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) { 01567 ++a_pData; 01568 } 01569 01570 // if it's an invalid line, just skip it 01571 if (*a_pData != '=') { 01572 continue; 01573 } 01574 01575 // empty keys are invalid 01576 if (a_pKey == a_pData) { 01577 while (*a_pData && !IsNewLineChar(*a_pData)) { 01578 ++a_pData; 01579 } 01580 continue; 01581 } 01582 01583 // remove trailing spaces from the key 01584 pTrail = a_pData - 1; 01585 while (pTrail >= a_pKey && IsSpace(*pTrail)) { 01586 --pTrail; 01587 } 01588 ++pTrail; 01589 *pTrail = 0; 01590 01591 // skip leading whitespace on the value 01592 ++a_pData; // safe as checked that it == '=' above 01593 while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) { 01594 ++a_pData; 01595 } 01596 01597 // find the end of the value which is the end of this line 01598 a_pVal = a_pData; 01599 while (*a_pData && !IsNewLineChar(*a_pData)) { 01600 ++a_pData; 01601 } 01602 01603 // remove trailing spaces from the value 01604 pTrail = a_pData - 1; 01605 if (*a_pData) { // prepare for the next round 01606 SkipNewLine(a_pData); 01607 } 01608 while (pTrail >= a_pVal && IsSpace(*pTrail)) { 01609 --pTrail; 01610 } 01611 ++pTrail; 01612 *pTrail = 0; 01613 01614 // check for multi-line entries 01615 if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) { 01616 // skip the "<<<" to get the tag that will end the multiline 01617 const SI_CHAR * pTagName = a_pVal + 3; 01618 return LoadMultiLineText(a_pData, a_pVal, pTagName); 01619 } 01620 01621 // return the standard entry 01622 return true; 01623 } 01624 01625 return false; 01626 } 01627 01628 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01629 bool 01630 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineTag( 01631 const SI_CHAR * a_pVal 01632 ) const 01633 { 01634 // check for the "<<<" prefix for a multi-line entry 01635 if (*a_pVal++ != '<') return false; 01636 if (*a_pVal++ != '<') return false; 01637 if (*a_pVal++ != '<') return false; 01638 return true; 01639 } 01640 01641 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01642 bool 01643 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData( 01644 const SI_CHAR * a_pData 01645 ) const 01646 { 01647 // data is multi-line if it has any of the following features: 01648 // * whitespace prefix 01649 // * embedded newlines 01650 // * whitespace suffix 01651 01652 // empty string 01653 if (!*a_pData) { 01654 return false; 01655 } 01656 01657 // check for prefix 01658 if (IsSpace(*a_pData)) { 01659 return true; 01660 } 01661 01662 // embedded newlines 01663 while (*a_pData) { 01664 if (IsNewLineChar(*a_pData)) { 01665 return true; 01666 } 01667 ++a_pData; 01668 } 01669 01670 // check for suffix 01671 if (IsSpace(*--a_pData)) { 01672 return true; 01673 } 01674 01675 return false; 01676 } 01677 01678 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01679 bool 01680 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar( 01681 SI_CHAR a_c 01682 ) const 01683 { 01684 return (a_c == '\n' || a_c == '\r'); 01685 } 01686 01687 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01688 bool 01689 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText( 01690 SI_CHAR *& a_pData, 01691 const SI_CHAR *& a_pVal, 01692 const SI_CHAR * a_pTagName, 01693 bool a_bAllowBlankLinesInComment 01694 ) const 01695 { 01696 // we modify this data to strip all newlines down to a single '\n' 01697 // character. This means that on Windows we need to strip out some 01698 // characters which will make the data shorter. 01699 // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become 01700 // LINE1-LINE1\nLINE2-LINE2\0 01701 // The pDataLine entry is the pointer to the location in memory that 01702 // the current line needs to start to run following the existing one. 01703 // This may be the same as pCurrLine in which case no move is needed. 01704 SI_CHAR * pDataLine = a_pData; 01705 SI_CHAR * pCurrLine; 01706 01707 // value starts at the current line 01708 a_pVal = a_pData; 01709 01710 // find the end tag. This tag must start in column 1 and be 01711 // followed by a newline. No whitespace removal is done while 01712 // searching for this tag. 01713 SI_CHAR cEndOfLineChar = *a_pData; 01714 for(;;) { 01715 // if we are loading comments then we need a comment character as 01716 // the first character on every line 01717 if (!a_pTagName && !IsComment(*a_pData)) { 01718 // if we aren't allowing blank lines then we're done 01719 if (!a_bAllowBlankLinesInComment) { 01720 break; 01721 } 01722 01723 // if we are allowing blank lines then we only include them 01724 // in this comment if another comment follows, so read ahead 01725 // to find out. 01726 SI_CHAR * pCurr = a_pData; 01727 int nNewLines = 0; 01728 while (IsSpace(*pCurr)) { 01729 if (IsNewLineChar(*pCurr)) { 01730 ++nNewLines; 01731 SkipNewLine(pCurr); 01732 } 01733 else { 01734 ++pCurr; 01735 } 01736 } 01737 01738 // we have a comment, add the blank lines to the output 01739 // and continue processing from here 01740 if (IsComment(*pCurr)) { 01741 for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n'; 01742 a_pData = pCurr; 01743 continue; 01744 } 01745 01746 // the comment ends here 01747 break; 01748 } 01749 01750 // find the end of this line 01751 pCurrLine = a_pData; 01752 while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData; 01753 01754 // move this line down to the location that it should be if necessary 01755 if (pDataLine < pCurrLine) { 01756 size_t nLen = (size_t) (a_pData - pCurrLine); 01757 memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR)); 01758 pDataLine[nLen] = '\0'; 01759 } 01760 01761 // end the line with a NULL 01762 cEndOfLineChar = *a_pData; 01763 *a_pData = 0; 01764 01765 // if are looking for a tag then do the check now. This is done before 01766 // checking for end of the data, so that if we have the tag at the end 01767 // of the data then the tag is removed correctly. 01768 if (a_pTagName && 01769 (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine))) 01770 { 01771 break; 01772 } 01773 01774 // if we are at the end of the data then we just automatically end 01775 // this entry and return the current data. 01776 if (!cEndOfLineChar) { 01777 return true; 01778 } 01779 01780 // otherwise we need to process this newline to ensure that it consists 01781 // of just a single \n character. 01782 pDataLine += (a_pData - pCurrLine); 01783 *a_pData = cEndOfLineChar; 01784 SkipNewLine(a_pData); 01785 *pDataLine++ = '\n'; 01786 } 01787 01788 // if we didn't find a comment at all then return false 01789 if (a_pVal == a_pData) { 01790 a_pVal = NULL; 01791 return false; 01792 } 01793 01794 // the data (which ends at the end of the last line) needs to be 01795 // null-terminated BEFORE before the newline character(s). If the 01796 // user wants a new line in the multi-line data then they need to 01797 // add an empty line before the tag. 01798 *--pDataLine = '\0'; 01799 01800 // if looking for a tag and if we aren't at the end of the data, 01801 // then move a_pData to the start of the next line. 01802 if (a_pTagName && cEndOfLineChar) { 01803 SI_ASSERT(IsNewLineChar(cEndOfLineChar)); 01804 *a_pData = cEndOfLineChar; 01805 SkipNewLine(a_pData); 01806 } 01807 01808 return true; 01809 } 01810 01811 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01812 SI_Error 01813 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString( 01814 const SI_CHAR *& a_pString 01815 ) 01816 { 01817 size_t uLen = 0; 01818 if (sizeof(SI_CHAR) == sizeof(char)) { 01819 uLen = strlen((const char *)a_pString); 01820 } 01821 else if (sizeof(SI_CHAR) == sizeof(wchar_t)) { 01822 uLen = wcslen((const wchar_t *)a_pString); 01823 } 01824 else { 01825 for ( ; a_pString[uLen]; ++uLen) /*loop*/ ; 01826 } 01827 ++uLen; // NULL character 01828 SI_CHAR * pCopy = new SI_CHAR[uLen]; 01829 if (!pCopy) { 01830 return SI_NOMEM; 01831 } 01832 memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen); 01833 m_strings.push_back(pCopy); 01834 a_pString = pCopy; 01835 return SI_OK; 01836 } 01837 01838 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01839 SI_Error 01840 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry( 01841 const SI_CHAR * a_pSection, 01842 const SI_CHAR * a_pKey, 01843 const SI_CHAR * a_pValue, 01844 const SI_CHAR * a_pComment, 01845 bool a_bForceReplace, 01846 bool a_bCopyStrings 01847 ) 01848 { 01849 SI_Error rc; 01850 bool bInserted = false; 01851 01852 SI_ASSERT(!a_pComment || IsComment(*a_pComment)); 01853 01854 // if we are copying strings then make a copy of the comment now 01855 // because we will need it when we add the entry. 01856 if (a_bCopyStrings && a_pComment) { 01857 rc = CopyString(a_pComment); 01858 if (rc < 0) return rc; 01859 } 01860 01861 // create the section entry if necessary 01862 typename TSection::iterator iSection = m_data.find(a_pSection); 01863 if (iSection == m_data.end()) { 01864 // if the section doesn't exist then we need a copy as the 01865 // string needs to last beyond the end of this function 01866 if (a_bCopyStrings) { 01867 rc = CopyString(a_pSection); 01868 if (rc < 0) return rc; 01869 } 01870 01871 // only set the comment if this is a section only entry 01872 Entry oSection(a_pSection, ++m_nOrder); 01873 if (a_pComment && (!a_pKey || !a_pValue)) { 01874 oSection.pComment = a_pComment; 01875 } 01876 01877 typename TSection::value_type oEntry(oSection, TKeyVal()); 01878 typedef typename TSection::iterator SectionIterator; 01879 std::pair<SectionIterator,bool> i = m_data.insert(oEntry); 01880 iSection = i.first; 01881 bInserted = true; 01882 } 01883 if (!a_pKey || !a_pValue) { 01884 // section only entries are specified with pItem and pVal as NULL 01885 return bInserted ? SI_INSERTED : SI_UPDATED; 01886 } 01887 01888 // check for existence of the key 01889 TKeyVal & keyval = iSection->second; 01890 typename TKeyVal::iterator iKey = keyval.find(a_pKey); 01891 01892 // remove all existing entries but save the load order and 01893 // comment of the first entry 01894 int nLoadOrder = ++m_nOrder; 01895 if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) { 01896 const SI_CHAR * pComment = NULL; 01897 while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) { 01898 if (iKey->first.nOrder < nLoadOrder) { 01899 nLoadOrder = iKey->first.nOrder; 01900 pComment = iKey->first.pComment; 01901 } 01902 ++iKey; 01903 } 01904 if (pComment) { 01905 DeleteString(a_pComment); 01906 a_pComment = pComment; 01907 CopyString(a_pComment); 01908 } 01909 Delete(a_pSection, a_pKey); 01910 iKey = keyval.end(); 01911 } 01912 01913 // make string copies if necessary 01914 bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace; 01915 if (a_bCopyStrings) { 01916 if (bForceCreateNewKey || iKey == keyval.end()) { 01917 // if the key doesn't exist then we need a copy as the 01918 // string needs to last beyond the end of this function 01919 // because we will be inserting the key next 01920 rc = CopyString(a_pKey); 01921 if (rc < 0) return rc; 01922 } 01923 01924 // we always need a copy of the value 01925 rc = CopyString(a_pValue); 01926 if (rc < 0) return rc; 01927 } 01928 01929 // create the key entry 01930 if (iKey == keyval.end() || bForceCreateNewKey) { 01931 Entry oKey(a_pKey, nLoadOrder); 01932 if (a_pComment) { 01933 oKey.pComment = a_pComment; 01934 } 01935 typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL)); 01936 iKey = keyval.insert(oEntry); 01937 bInserted = true; 01938 } 01939 iKey->second = a_pValue; 01940 return bInserted ? SI_INSERTED : SI_UPDATED; 01941 } 01942 01943 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01944 const SI_CHAR * 01945 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetValue( 01946 const SI_CHAR * a_pSection, 01947 const SI_CHAR * a_pKey, 01948 const SI_CHAR * a_pDefault, 01949 bool * a_pHasMultiple 01950 ) const 01951 { 01952 if (a_pHasMultiple) { 01953 *a_pHasMultiple = false; 01954 } 01955 if (!a_pSection || !a_pKey) { 01956 return a_pDefault; 01957 } 01958 typename TSection::const_iterator iSection = m_data.find(a_pSection); 01959 if (iSection == m_data.end()) { 01960 return a_pDefault; 01961 } 01962 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey); 01963 if (iKeyVal == iSection->second.end()) { 01964 return a_pDefault; 01965 } 01966 01967 // check for multiple entries with the same key 01968 if (m_bAllowMultiKey && a_pHasMultiple) { 01969 typename TKeyVal::const_iterator iTemp = iKeyVal; 01970 if (++iTemp != iSection->second.end()) { 01971 if (!IsLess(a_pKey, iTemp->first.pItem)) { 01972 *a_pHasMultiple = true; 01973 } 01974 } 01975 } 01976 01977 return iKeyVal->second; 01978 } 01979 01980 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 01981 long 01982 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetLongValue( 01983 const SI_CHAR * a_pSection, 01984 const SI_CHAR * a_pKey, 01985 long a_nDefault, 01986 bool * a_pHasMultiple 01987 ) const 01988 { 01989 // return the default if we don't have a value 01990 const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); 01991 if (!pszValue || !*pszValue) return a_nDefault; 01992 01993 // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII 01994 char szValue[64] = { 0 }; 01995 SI_CONVERTER c(m_bStoreIsUtf8); 01996 if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) { 01997 return a_nDefault; 01998 } 01999 02000 // handle the value as hex if prefaced with "0x" 02001 long nValue = a_nDefault; 02002 char * pszSuffix = szValue; 02003 if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) { 02004 if (!szValue[2]) return a_nDefault; 02005 nValue = strtol(&szValue[2], &pszSuffix, 16); 02006 } 02007 else { 02008 nValue = strtol(szValue, &pszSuffix, 10); 02009 } 02010 02011 // any invalid strings will return the default value 02012 if (*pszSuffix) { 02013 return a_nDefault; 02014 } 02015 02016 return nValue; 02017 } 02018 02019 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02020 SI_Error 02021 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue( 02022 const SI_CHAR * a_pSection, 02023 const SI_CHAR * a_pKey, 02024 long a_nValue, 02025 const SI_CHAR * a_pComment, 02026 bool a_bUseHex, 02027 bool a_bForceReplace 02028 ) 02029 { 02030 // use SetValue to create sections 02031 if (!a_pSection || !a_pKey) return SI_FAIL; 02032 02033 // convert to an ASCII string 02034 char szInput[64]; 02035 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE 02036 sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue); 02037 #else // !__STDC_WANT_SECURE_LIB__ 02038 sprintf(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue); 02039 #endif // __STDC_WANT_SECURE_LIB__ 02040 02041 // convert to output text 02042 SI_CHAR szOutput[64]; 02043 SI_CONVERTER c(m_bStoreIsUtf8); 02044 c.ConvertFromStore(szInput, strlen(szInput) + 1, 02045 szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); 02046 02047 // actually add it 02048 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); 02049 } 02050 02051 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02052 double 02053 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetDoubleValue( 02054 const SI_CHAR * a_pSection, 02055 const SI_CHAR * a_pKey, 02056 double a_nDefault, 02057 bool * a_pHasMultiple 02058 ) const 02059 { 02060 // return the default if we don't have a value 02061 const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); 02062 if (!pszValue || !*pszValue) return a_nDefault; 02063 02064 // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII 02065 char szValue[64] = { 0 }; 02066 SI_CONVERTER c(m_bStoreIsUtf8); 02067 if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) { 02068 return a_nDefault; 02069 } 02070 02071 char * pszSuffix = NULL; 02072 double nValue = strtod(szValue, &pszSuffix); 02073 02074 // any invalid strings will return the default value 02075 if (!pszSuffix || *pszSuffix) { 02076 return a_nDefault; 02077 } 02078 02079 return nValue; 02080 } 02081 02082 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02083 SI_Error 02084 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetDoubleValue( 02085 const SI_CHAR * a_pSection, 02086 const SI_CHAR * a_pKey, 02087 double a_nValue, 02088 const SI_CHAR * a_pComment, 02089 bool a_bForceReplace 02090 ) 02091 { 02092 // use SetValue to create sections 02093 if (!a_pSection || !a_pKey) return SI_FAIL; 02094 02095 // convert to an ASCII string 02096 char szInput[64]; 02097 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE 02098 sprintf_s(szInput, "%f", a_nValue); 02099 #else // !__STDC_WANT_SECURE_LIB__ 02100 sprintf(szInput, "%f", a_nValue); 02101 #endif // __STDC_WANT_SECURE_LIB__ 02102 02103 // convert to output text 02104 SI_CHAR szOutput[64]; 02105 SI_CONVERTER c(m_bStoreIsUtf8); 02106 c.ConvertFromStore(szInput, strlen(szInput) + 1, 02107 szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); 02108 02109 // actually add it 02110 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); 02111 } 02112 02113 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02114 bool 02115 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetBoolValue( 02116 const SI_CHAR * a_pSection, 02117 const SI_CHAR * a_pKey, 02118 bool a_bDefault, 02119 bool * a_pHasMultiple 02120 ) const 02121 { 02122 // return the default if we don't have a value 02123 const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); 02124 if (!pszValue || !*pszValue) return a_bDefault; 02125 02126 // we only look at the minimum number of characters 02127 switch (pszValue[0]) { 02128 case 't': case 'T': // true 02129 case 'y': case 'Y': // yes 02130 case '1': // 1 (one) 02131 return true; 02132 02133 case 'f': case 'F': // false 02134 case 'n': case 'N': // no 02135 case '0': // 0 (zero) 02136 return false; 02137 02138 case 'o': case 'O': 02139 if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on 02140 if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off 02141 break; 02142 } 02143 02144 // no recognized value, return the default 02145 return a_bDefault; 02146 } 02147 02148 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02149 SI_Error 02150 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetBoolValue( 02151 const SI_CHAR * a_pSection, 02152 const SI_CHAR * a_pKey, 02153 bool a_bValue, 02154 const SI_CHAR * a_pComment, 02155 bool a_bForceReplace 02156 ) 02157 { 02158 // use SetValue to create sections 02159 if (!a_pSection || !a_pKey) return SI_FAIL; 02160 02161 // convert to an ASCII string 02162 const char * pszInput = a_bValue ? "true" : "false"; 02163 02164 // convert to output text 02165 SI_CHAR szOutput[64]; 02166 SI_CONVERTER c(m_bStoreIsUtf8); 02167 c.ConvertFromStore(pszInput, strlen(pszInput) + 1, 02168 szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); 02169 02170 // actually add it 02171 return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); 02172 } 02173 02174 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02175 bool 02176 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues( 02177 const SI_CHAR * a_pSection, 02178 const SI_CHAR * a_pKey, 02179 TNamesDepend & a_values 02180 ) const 02181 { 02182 a_values.clear(); 02183 02184 if (!a_pSection || !a_pKey) { 02185 return false; 02186 } 02187 typename TSection::const_iterator iSection = m_data.find(a_pSection); 02188 if (iSection == m_data.end()) { 02189 return false; 02190 } 02191 typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey); 02192 if (iKeyVal == iSection->second.end()) { 02193 return false; 02194 } 02195 02196 // insert all values for this key 02197 a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder)); 02198 if (m_bAllowMultiKey) { 02199 ++iKeyVal; 02200 while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) { 02201 a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder)); 02202 ++iKeyVal; 02203 } 02204 } 02205 02206 return true; 02207 } 02208 02209 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02210 int 02211 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSectionSize( 02212 const SI_CHAR * a_pSection 02213 ) const 02214 { 02215 if (!a_pSection) { 02216 return -1; 02217 } 02218 02219 typename TSection::const_iterator iSection = m_data.find(a_pSection); 02220 if (iSection == m_data.end()) { 02221 return -1; 02222 } 02223 const TKeyVal & section = iSection->second; 02224 02225 // if multi-key isn't permitted then the section size is 02226 // the number of keys that we have. 02227 if (!m_bAllowMultiKey || section.empty()) { 02228 return (int) section.size(); 02229 } 02230 02231 // otherwise we need to count them 02232 int nCount = 0; 02233 const SI_CHAR * pLastKey = NULL; 02234 typename TKeyVal::const_iterator iKeyVal = section.begin(); 02235 for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) { 02236 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) { 02237 ++nCount; 02238 pLastKey = iKeyVal->first.pItem; 02239 } 02240 } 02241 return nCount; 02242 } 02243 02244 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02245 const typename CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::TKeyVal * 02246 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSection( 02247 const SI_CHAR * a_pSection 02248 ) const 02249 { 02250 if (a_pSection) { 02251 typename TSection::const_iterator i = m_data.find(a_pSection); 02252 if (i != m_data.end()) { 02253 return &(i->second); 02254 } 02255 } 02256 return 0; 02257 } 02258 02259 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02260 void 02261 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections( 02262 TNamesDepend & a_names 02263 ) const 02264 { 02265 a_names.clear(); 02266 typename TSection::const_iterator i = m_data.begin(); 02267 for (int n = 0; i != m_data.end(); ++i, ++n ) { 02268 a_names.push_back(i->first); 02269 } 02270 } 02271 02272 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02273 bool 02274 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllKeys( 02275 const SI_CHAR * a_pSection, 02276 TNamesDepend & a_names 02277 ) const 02278 { 02279 a_names.clear(); 02280 02281 if (!a_pSection) { 02282 return false; 02283 } 02284 02285 typename TSection::const_iterator iSection = m_data.find(a_pSection); 02286 if (iSection == m_data.end()) { 02287 return false; 02288 } 02289 02290 const TKeyVal & section = iSection->second; 02291 const SI_CHAR * pLastKey = NULL; 02292 typename TKeyVal::const_iterator iKeyVal = section.begin(); 02293 for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) { 02294 if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) { 02295 a_names.push_back(iKeyVal->first); 02296 pLastKey = iKeyVal->first.pItem; 02297 } 02298 } 02299 02300 return true; 02301 } 02302 02303 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02304 SI_Error 02305 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile( 02306 const char * a_pszFile, 02307 bool a_bAddSignature 02308 ) const 02309 { 02310 FILE * fp = NULL; 02311 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE 02312 fopen_s(&fp, a_pszFile, "wb"); 02313 #else // !__STDC_WANT_SECURE_LIB__ 02314 fp = fopen(a_pszFile, "wb"); 02315 #endif // __STDC_WANT_SECURE_LIB__ 02316 if (!fp) return SI_FILE; 02317 SI_Error rc = SaveFile(fp, a_bAddSignature); 02318 fclose(fp); 02319 return rc; 02320 } 02321 02322 #ifdef SI_HAS_WIDE_FILE 02323 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02324 SI_Error 02325 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile( 02326 const SI_WCHAR_T * a_pwszFile, 02327 bool a_bAddSignature 02328 ) const 02329 { 02330 #ifdef _WIN32 02331 FILE * fp = NULL; 02332 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE 02333 _wfopen_s(&fp, a_pwszFile, L"wb"); 02334 #else // !__STDC_WANT_SECURE_LIB__ 02335 fp = _wfopen(a_pwszFile, L"wb"); 02336 #endif // __STDC_WANT_SECURE_LIB__ 02337 if (!fp) return SI_FILE; 02338 SI_Error rc = SaveFile(fp, a_bAddSignature); 02339 fclose(fp); 02340 return rc; 02341 #else // !_WIN32 (therefore SI_CONVERT_ICU) 02342 char szFile[256]; 02343 u_austrncpy(szFile, a_pwszFile, sizeof(szFile)); 02344 return SaveFile(szFile, a_bAddSignature); 02345 #endif // _WIN32 02346 } 02347 #endif // SI_HAS_WIDE_FILE 02348 02349 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02350 SI_Error 02351 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile( 02352 FILE * a_pFile, 02353 bool a_bAddSignature 02354 ) const 02355 { 02356 FileWriter writer(a_pFile); 02357 return Save(writer, a_bAddSignature); 02358 } 02359 02360 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02361 SI_Error 02362 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save( 02363 OutputWriter & a_oOutput, 02364 bool a_bAddSignature 02365 ) const 02366 { 02367 Converter convert(m_bStoreIsUtf8); 02368 02369 // add the UTF-8 signature if it is desired 02370 if (m_bStoreIsUtf8 && a_bAddSignature) { 02371 a_oOutput.Write(SI_UTF8_SIGNATURE); 02372 } 02373 02374 // get all of the sections sorted in load order 02375 TNamesDepend oSections; 02376 GetAllSections(oSections); 02377 #if defined(_MSC_VER) && _MSC_VER <= 1200 02378 oSections.sort(); 02379 #elif defined(__BORLANDC__) 02380 oSections.sort(Entry::LoadOrder()); 02381 #else 02382 oSections.sort(typename Entry::LoadOrder()); 02383 #endif 02384 02385 // write the file comment if we have one 02386 bool bNeedNewLine = false; 02387 if (m_pFileComment) { 02388 if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) { 02389 return SI_FAIL; 02390 } 02391 bNeedNewLine = true; 02392 } 02393 02394 // iterate through our sections and output the data 02395 typename TNamesDepend::const_iterator iSection = oSections.begin(); 02396 for ( ; iSection != oSections.end(); ++iSection ) { 02397 // write out the comment if there is one 02398 if (iSection->pComment) { 02399 if (bNeedNewLine) { 02400 a_oOutput.Write(SI_NEWLINE_A); 02401 a_oOutput.Write(SI_NEWLINE_A); 02402 } 02403 if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) { 02404 return SI_FAIL; 02405 } 02406 bNeedNewLine = false; 02407 } 02408 02409 if (bNeedNewLine) { 02410 a_oOutput.Write(SI_NEWLINE_A); 02411 a_oOutput.Write(SI_NEWLINE_A); 02412 bNeedNewLine = false; 02413 } 02414 02415 // write the section (unless there is no section name) 02416 if (*iSection->pItem) { 02417 if (!convert.ConvertToStore(iSection->pItem)) { 02418 return SI_FAIL; 02419 } 02420 a_oOutput.Write("["); 02421 a_oOutput.Write(convert.Data()); 02422 a_oOutput.Write("]"); 02423 a_oOutput.Write(SI_NEWLINE_A); 02424 } 02425 02426 // get all of the keys sorted in load order 02427 TNamesDepend oKeys; 02428 GetAllKeys(iSection->pItem, oKeys); 02429 #if defined(_MSC_VER) && _MSC_VER <= 1200 02430 oKeys.sort(); 02431 #elif defined(__BORLANDC__) 02432 oKeys.sort(Entry::LoadOrder()); 02433 #else 02434 oKeys.sort(typename Entry::LoadOrder()); 02435 #endif 02436 02437 // write all keys and values 02438 typename TNamesDepend::const_iterator iKey = oKeys.begin(); 02439 for ( ; iKey != oKeys.end(); ++iKey) { 02440 // get all values for this key 02441 TNamesDepend oValues; 02442 GetAllValues(iSection->pItem, iKey->pItem, oValues); 02443 02444 typename TNamesDepend::const_iterator iValue = oValues.begin(); 02445 for ( ; iValue != oValues.end(); ++iValue) { 02446 // write out the comment if there is one 02447 if (iValue->pComment) { 02448 a_oOutput.Write(SI_NEWLINE_A); 02449 if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) { 02450 return SI_FAIL; 02451 } 02452 } 02453 02454 // write the key 02455 if (!convert.ConvertToStore(iKey->pItem)) { 02456 return SI_FAIL; 02457 } 02458 a_oOutput.Write(convert.Data()); 02459 02460 // write the value 02461 if (!convert.ConvertToStore(iValue->pItem)) { 02462 return SI_FAIL; 02463 } 02464 a_oOutput.Write(m_bSpaces ? " = " : "="); 02465 if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) { 02466 // multi-line data needs to be processed specially to ensure 02467 // that we use the correct newline format for the current system 02468 a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A); 02469 if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) { 02470 return SI_FAIL; 02471 } 02472 a_oOutput.Write("END_OF_TEXT"); 02473 } 02474 else { 02475 a_oOutput.Write(convert.Data()); 02476 } 02477 a_oOutput.Write(SI_NEWLINE_A); 02478 } 02479 } 02480 02481 bNeedNewLine = true; 02482 } 02483 02484 return SI_OK; 02485 } 02486 02487 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02488 bool 02489 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::OutputMultiLineText( 02490 OutputWriter & a_oOutput, 02491 Converter & a_oConverter, 02492 const SI_CHAR * a_pText 02493 ) const 02494 { 02495 const SI_CHAR * pEndOfLine; 02496 SI_CHAR cEndOfLineChar = *a_pText; 02497 while (cEndOfLineChar) { 02498 // find the end of this line 02499 pEndOfLine = a_pText; 02500 for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ; 02501 cEndOfLineChar = *pEndOfLine; 02502 02503 // temporarily null terminate, convert and output the line 02504 *const_cast<SI_CHAR*>(pEndOfLine) = 0; 02505 if (!a_oConverter.ConvertToStore(a_pText)) { 02506 return false; 02507 } 02508 *const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar; 02509 a_pText += (pEndOfLine - a_pText) + 1; 02510 a_oOutput.Write(a_oConverter.Data()); 02511 a_oOutput.Write(SI_NEWLINE_A); 02512 } 02513 return true; 02514 } 02515 02516 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02517 bool 02518 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete( 02519 const SI_CHAR * a_pSection, 02520 const SI_CHAR * a_pKey, 02521 bool a_bRemoveEmpty 02522 ) 02523 { 02524 if (!a_pSection) { 02525 return false; 02526 } 02527 02528 typename TSection::iterator iSection = m_data.find(a_pSection); 02529 if (iSection == m_data.end()) { 02530 return false; 02531 } 02532 02533 // remove a single key if we have a keyname 02534 if (a_pKey) { 02535 typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey); 02536 if (iKeyVal == iSection->second.end()) { 02537 return false; 02538 } 02539 02540 // remove any copied strings and then the key 02541 typename TKeyVal::iterator iDelete; 02542 do { 02543 iDelete = iKeyVal++; 02544 02545 DeleteString(iDelete->first.pItem); 02546 DeleteString(iDelete->second); 02547 iSection->second.erase(iDelete); 02548 } 02549 while (iKeyVal != iSection->second.end() 02550 && !IsLess(a_pKey, iKeyVal->first.pItem)); 02551 02552 // done now if the section is not empty or we are not pruning away 02553 // the empty sections. Otherwise let it fall through into the section 02554 // deletion code 02555 if (!a_bRemoveEmpty || !iSection->second.empty()) { 02556 return true; 02557 } 02558 } 02559 else { 02560 // delete all copied strings from this section. The actual 02561 // entries will be removed when the section is removed. 02562 typename TKeyVal::iterator iKeyVal = iSection->second.begin(); 02563 for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) { 02564 DeleteString(iKeyVal->first.pItem); 02565 DeleteString(iKeyVal->second); 02566 } 02567 } 02568 02569 // delete the section itself 02570 DeleteString(iSection->first.pItem); 02571 m_data.erase(iSection); 02572 02573 return true; 02574 } 02575 02576 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> 02577 void 02578 CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString( 02579 const SI_CHAR * a_pString 02580 ) 02581 { 02582 // strings may exist either inside the data block, or they will be 02583 // individually allocated and stored in m_strings. We only physically 02584 // delete those stored in m_strings. 02585 if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) { 02586 typename TNamesDepend::iterator i = m_strings.begin(); 02587 for (;i != m_strings.end(); ++i) { 02588 if (a_pString == i->pItem) { 02589 delete[] const_cast<SI_CHAR*>(i->pItem); 02590 m_strings.erase(i); 02591 break; 02592 } 02593 } 02594 } 02595 } 02596 02597 // --------------------------------------------------------------------------- 02598 // CONVERSION FUNCTIONS 02599 // --------------------------------------------------------------------------- 02600 02601 // Defines the conversion classes for different libraries. Before including 02602 // SimpleIni.h, set the converter that you wish you use by defining one of the 02603 // following symbols. 02604 // 02605 // SI_CONVERT_GENERIC Use the Unicode reference conversion library in 02606 // the accompanying files ConvertUTF.h/c 02607 // SI_CONVERT_ICU Use the IBM ICU conversion library. Requires 02608 // ICU headers on include path and icuuc.lib 02609 // SI_CONVERT_WIN32 Use the Win32 API functions for conversion. 02610 02611 #if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU) 02612 # ifdef _WIN32 02613 # define SI_CONVERT_WIN32 02614 # else 02615 # define SI_CONVERT_GENERIC 02616 # endif 02617 #endif 02618 02624 template<class SI_CHAR> 02625 struct SI_GenericCase { 02626 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { 02627 long cmp; 02628 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) { 02629 cmp = (long) *pLeft - (long) *pRight; 02630 if (cmp != 0) { 02631 return cmp < 0; 02632 } 02633 } 02634 return *pRight != 0; 02635 } 02636 }; 02637 02644 template<class SI_CHAR> 02645 struct SI_GenericNoCase { 02646 inline SI_CHAR locase(SI_CHAR ch) const { 02647 return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a'); 02648 } 02649 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { 02650 long cmp; 02651 for ( ;*pLeft && *pRight; ++pLeft, ++pRight) { 02652 cmp = (long) locase(*pLeft) - (long) locase(*pRight); 02653 if (cmp != 0) { 02654 return cmp < 0; 02655 } 02656 } 02657 return *pRight != 0; 02658 } 02659 }; 02660 02664 template<class SI_CHAR> 02665 class SI_ConvertA { 02666 bool m_bStoreIsUtf8; 02667 protected: 02668 SI_ConvertA() { } 02669 public: 02670 SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { } 02671 02672 /* copy and assignment */ 02673 SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); } 02674 SI_ConvertA & operator=(const SI_ConvertA & rhs) { 02675 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8; 02676 return *this; 02677 } 02678 02692 size_t SizeFromStore( 02693 const char * a_pInputData, 02694 size_t a_uInputDataLen) 02695 { 02696 (void)a_pInputData; 02697 SI_ASSERT(a_uInputDataLen != (size_t) -1); 02698 02699 // ASCII/MBCS/UTF-8 needs no conversion 02700 return a_uInputDataLen; 02701 } 02702 02716 bool ConvertFromStore( 02717 const char * a_pInputData, 02718 size_t a_uInputDataLen, 02719 SI_CHAR * a_pOutputData, 02720 size_t a_uOutputDataSize) 02721 { 02722 // ASCII/MBCS/UTF-8 needs no conversion 02723 if (a_uInputDataLen > a_uOutputDataSize) { 02724 return false; 02725 } 02726 memcpy(a_pOutputData, a_pInputData, a_uInputDataLen); 02727 return true; 02728 } 02729 02740 size_t SizeToStore( 02741 const SI_CHAR * a_pInputData) 02742 { 02743 // ASCII/MBCS/UTF-8 needs no conversion 02744 return strlen((const char *)a_pInputData) + 1; 02745 } 02746 02760 bool ConvertToStore( 02761 const SI_CHAR * a_pInputData, 02762 char * a_pOutputData, 02763 size_t a_uOutputDataSize) 02764 { 02765 // calc input string length (SI_CHAR type and size independent) 02766 size_t uInputLen = strlen((const char *)a_pInputData) + 1; 02767 if (uInputLen > a_uOutputDataSize) { 02768 return false; 02769 } 02770 02771 // ascii/UTF-8 needs no conversion 02772 memcpy(a_pOutputData, a_pInputData, uInputLen); 02773 return true; 02774 } 02775 }; 02776 02777 02778 // --------------------------------------------------------------------------- 02779 // SI_CONVERT_GENERIC 02780 // --------------------------------------------------------------------------- 02781 #ifdef SI_CONVERT_GENERIC 02782 02783 #define SI_Case SI_GenericCase 02784 #define SI_NoCase SI_GenericNoCase 02785 02786 #include <wchar.h> 02787 #include "ConvertUTF.h" 02788 02793 template<class SI_CHAR> 02794 class SI_ConvertW { 02795 bool m_bStoreIsUtf8; 02796 protected: 02797 SI_ConvertW() { } 02798 public: 02799 SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { } 02800 02801 /* copy and assignment */ 02802 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } 02803 SI_ConvertW & operator=(const SI_ConvertW & rhs) { 02804 m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8; 02805 return *this; 02806 } 02807 02821 size_t SizeFromStore( 02822 const char * a_pInputData, 02823 size_t a_uInputDataLen) 02824 { 02825 SI_ASSERT(a_uInputDataLen != (size_t) -1); 02826 02827 if (m_bStoreIsUtf8) { 02828 // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t 02829 // so we just return the same number of characters required as for 02830 // the source text. 02831 return a_uInputDataLen; 02832 } 02833 else { 02834 return mbstowcs(NULL, a_pInputData, a_uInputDataLen); 02835 } 02836 } 02837 02851 bool ConvertFromStore( 02852 const char * a_pInputData, 02853 size_t a_uInputDataLen, 02854 SI_CHAR * a_pOutputData, 02855 size_t a_uOutputDataSize) 02856 { 02857 if (m_bStoreIsUtf8) { 02858 // This uses the Unicode reference implementation to do the 02859 // conversion from UTF-8 to wchar_t. The required files are 02860 // ConvertUTF.h and ConvertUTF.c which should be included in 02861 // the distribution but are publically available from unicode.org 02862 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/ 02863 ConversionResult retval; 02864 const UTF8 * pUtf8 = (const UTF8 *) a_pInputData; 02865 if (sizeof(wchar_t) == sizeof(UTF32)) { 02866 UTF32 * pUtf32 = (UTF32 *) a_pOutputData; 02867 retval = ConvertUTF8toUTF32( 02868 &pUtf8, pUtf8 + a_uInputDataLen, 02869 &pUtf32, pUtf32 + a_uOutputDataSize, 02870 lenientConversion); 02871 } 02872 else if (sizeof(wchar_t) == sizeof(UTF16)) { 02873 UTF16 * pUtf16 = (UTF16 *) a_pOutputData; 02874 retval = ConvertUTF8toUTF16( 02875 &pUtf8, pUtf8 + a_uInputDataLen, 02876 &pUtf16, pUtf16 + a_uOutputDataSize, 02877 lenientConversion); 02878 } 02879 return retval == conversionOK; 02880 } 02881 else { 02882 size_t retval = mbstowcs(a_pOutputData, 02883 a_pInputData, a_uOutputDataSize); 02884 return retval != (size_t)(-1); 02885 } 02886 } 02887 02898 size_t SizeToStore( 02899 const SI_CHAR * a_pInputData) 02900 { 02901 if (m_bStoreIsUtf8) { 02902 // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char 02903 size_t uLen = 0; 02904 while (a_pInputData[uLen]) { 02905 ++uLen; 02906 } 02907 return (6 * uLen) + 1; 02908 } 02909 else { 02910 size_t uLen = wcstombs(NULL, a_pInputData, 0); 02911 if (uLen == (size_t)(-1)) { 02912 return uLen; 02913 } 02914 return uLen + 1; // include NULL terminator 02915 } 02916 } 02917 02931 bool ConvertToStore( 02932 const SI_CHAR * a_pInputData, 02933 char * a_pOutputData, 02934 size_t a_uOutputDataSize 02935 ) 02936 { 02937 if (m_bStoreIsUtf8) { 02938 // calc input string length (SI_CHAR type and size independent) 02939 size_t uInputLen = 0; 02940 while (a_pInputData[uInputLen]) { 02941 ++uInputLen; 02942 } 02943 ++uInputLen; // include the NULL char 02944 02945 // This uses the Unicode reference implementation to do the 02946 // conversion from wchar_t to UTF-8. The required files are 02947 // ConvertUTF.h and ConvertUTF.c which should be included in 02948 // the distribution but are publically available from unicode.org 02949 // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/ 02950 ConversionResult retval; 02951 UTF8 * pUtf8 = (UTF8 *) a_pOutputData; 02952 if (sizeof(wchar_t) == sizeof(UTF32)) { 02953 const UTF32 * pUtf32 = (const UTF32 *) a_pInputData; 02954 retval = ConvertUTF32toUTF8( 02955 &pUtf32, pUtf32 + uInputLen, 02956 &pUtf8, pUtf8 + a_uOutputDataSize, 02957 lenientConversion); 02958 } 02959 else if (sizeof(wchar_t) == sizeof(UTF16)) { 02960 const UTF16 * pUtf16 = (const UTF16 *) a_pInputData; 02961 retval = ConvertUTF16toUTF8( 02962 &pUtf16, pUtf16 + uInputLen, 02963 &pUtf8, pUtf8 + a_uOutputDataSize, 02964 lenientConversion); 02965 } 02966 return retval == conversionOK; 02967 } 02968 else { 02969 size_t retval = wcstombs(a_pOutputData, 02970 a_pInputData, a_uOutputDataSize); 02971 return retval != (size_t) -1; 02972 } 02973 } 02974 }; 02975 02976 #endif // SI_CONVERT_GENERIC 02977 02978 02979 // --------------------------------------------------------------------------- 02980 // SI_CONVERT_ICU 02981 // --------------------------------------------------------------------------- 02982 #ifdef SI_CONVERT_ICU 02983 02984 #define SI_Case SI_GenericCase 02985 #define SI_NoCase SI_GenericNoCase 02986 02987 #include <unicode/ucnv.h> 02988 02992 template<class SI_CHAR> 02993 class SI_ConvertW { 02994 const char * m_pEncoding; 02995 UConverter * m_pConverter; 02996 protected: 02997 SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { } 02998 public: 02999 SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) { 03000 m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL; 03001 } 03002 03003 /* copy and assignment */ 03004 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } 03005 SI_ConvertW & operator=(const SI_ConvertW & rhs) { 03006 m_pEncoding = rhs.m_pEncoding; 03007 m_pConverter = NULL; 03008 return *this; 03009 } 03010 ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); } 03011 03025 size_t SizeFromStore( 03026 const char * a_pInputData, 03027 size_t a_uInputDataLen) 03028 { 03029 SI_ASSERT(a_uInputDataLen != (size_t) -1); 03030 03031 UErrorCode nError; 03032 03033 if (!m_pConverter) { 03034 nError = U_ZERO_ERROR; 03035 m_pConverter = ucnv_open(m_pEncoding, &nError); 03036 if (U_FAILURE(nError)) { 03037 return (size_t) -1; 03038 } 03039 } 03040 03041 nError = U_ZERO_ERROR; 03042 int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0, 03043 a_pInputData, (int32_t) a_uInputDataLen, &nError); 03044 if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) { 03045 return (size_t) -1; 03046 } 03047 03048 return (size_t) nLen; 03049 } 03050 03064 bool ConvertFromStore( 03065 const char * a_pInputData, 03066 size_t a_uInputDataLen, 03067 UChar * a_pOutputData, 03068 size_t a_uOutputDataSize) 03069 { 03070 UErrorCode nError; 03071 03072 if (!m_pConverter) { 03073 nError = U_ZERO_ERROR; 03074 m_pConverter = ucnv_open(m_pEncoding, &nError); 03075 if (U_FAILURE(nError)) { 03076 return false; 03077 } 03078 } 03079 03080 nError = U_ZERO_ERROR; 03081 ucnv_toUChars(m_pConverter, 03082 a_pOutputData, (int32_t) a_uOutputDataSize, 03083 a_pInputData, (int32_t) a_uInputDataLen, &nError); 03084 if (U_FAILURE(nError)) { 03085 return false; 03086 } 03087 03088 return true; 03089 } 03090 03101 size_t SizeToStore( 03102 const UChar * a_pInputData) 03103 { 03104 UErrorCode nError; 03105 03106 if (!m_pConverter) { 03107 nError = U_ZERO_ERROR; 03108 m_pConverter = ucnv_open(m_pEncoding, &nError); 03109 if (U_FAILURE(nError)) { 03110 return (size_t) -1; 03111 } 03112 } 03113 03114 nError = U_ZERO_ERROR; 03115 int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0, 03116 a_pInputData, -1, &nError); 03117 if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) { 03118 return (size_t) -1; 03119 } 03120 03121 return (size_t) nLen + 1; 03122 } 03123 03137 bool ConvertToStore( 03138 const UChar * a_pInputData, 03139 char * a_pOutputData, 03140 size_t a_uOutputDataSize) 03141 { 03142 UErrorCode nError; 03143 03144 if (!m_pConverter) { 03145 nError = U_ZERO_ERROR; 03146 m_pConverter = ucnv_open(m_pEncoding, &nError); 03147 if (U_FAILURE(nError)) { 03148 return false; 03149 } 03150 } 03151 03152 nError = U_ZERO_ERROR; 03153 ucnv_fromUChars(m_pConverter, 03154 a_pOutputData, (int32_t) a_uOutputDataSize, 03155 a_pInputData, -1, &nError); 03156 if (U_FAILURE(nError)) { 03157 return false; 03158 } 03159 03160 return true; 03161 } 03162 }; 03163 03164 #endif // SI_CONVERT_ICU 03165 03166 03167 // --------------------------------------------------------------------------- 03168 // SI_CONVERT_WIN32 03169 // --------------------------------------------------------------------------- 03170 #ifdef SI_CONVERT_WIN32 03171 03172 #define SI_Case SI_GenericCase 03173 03174 // Windows CE doesn't have errno or MBCS libraries 03175 #ifdef _WIN32_WCE 03176 # ifndef SI_NO_MBCS 03177 # define SI_NO_MBCS 03178 # endif 03179 #endif 03180 03181 #include <windows.h> 03182 #ifdef SI_NO_MBCS 03183 # define SI_NoCase SI_GenericNoCase 03184 #else // !SI_NO_MBCS 03185 03193 #include <mbstring.h> 03194 template<class SI_CHAR> 03195 struct SI_NoCase { 03196 bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { 03197 if (sizeof(SI_CHAR) == sizeof(char)) { 03198 return _mbsicmp((const unsigned char *)pLeft, 03199 (const unsigned char *)pRight) < 0; 03200 } 03201 if (sizeof(SI_CHAR) == sizeof(wchar_t)) { 03202 return _wcsicmp((const wchar_t *)pLeft, 03203 (const wchar_t *)pRight) < 0; 03204 } 03205 return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight); 03206 } 03207 }; 03208 #endif // SI_NO_MBCS 03209 03216 template<class SI_CHAR> 03217 class SI_ConvertW { 03218 UINT m_uCodePage; 03219 protected: 03220 SI_ConvertW() { } 03221 public: 03222 SI_ConvertW(bool a_bStoreIsUtf8) { 03223 m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP; 03224 } 03225 03226 /* copy and assignment */ 03227 SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } 03228 SI_ConvertW & operator=(const SI_ConvertW & rhs) { 03229 m_uCodePage = rhs.m_uCodePage; 03230 return *this; 03231 } 03232 03246 size_t SizeFromStore( 03247 const char * a_pInputData, 03248 size_t a_uInputDataLen) 03249 { 03250 SI_ASSERT(a_uInputDataLen != (size_t) -1); 03251 03252 int retval = MultiByteToWideChar( 03253 m_uCodePage, 0, 03254 a_pInputData, (int) a_uInputDataLen, 03255 0, 0); 03256 return (size_t)(retval > 0 ? retval : -1); 03257 } 03258 03272 bool ConvertFromStore( 03273 const char * a_pInputData, 03274 size_t a_uInputDataLen, 03275 SI_CHAR * a_pOutputData, 03276 size_t a_uOutputDataSize) 03277 { 03278 int nSize = MultiByteToWideChar( 03279 m_uCodePage, 0, 03280 a_pInputData, (int) a_uInputDataLen, 03281 (wchar_t *) a_pOutputData, (int) a_uOutputDataSize); 03282 return (nSize > 0); 03283 } 03284 03295 size_t SizeToStore( 03296 const SI_CHAR * a_pInputData) 03297 { 03298 int retval = WideCharToMultiByte( 03299 m_uCodePage, 0, 03300 (const wchar_t *) a_pInputData, -1, 03301 0, 0, 0, 0); 03302 return (size_t) (retval > 0 ? retval : -1); 03303 } 03304 03318 bool ConvertToStore( 03319 const SI_CHAR * a_pInputData, 03320 char * a_pOutputData, 03321 size_t a_uOutputDataSize) 03322 { 03323 int retval = WideCharToMultiByte( 03324 m_uCodePage, 0, 03325 (const wchar_t *) a_pInputData, -1, 03326 a_pOutputData, (int) a_uOutputDataSize, 0, 0); 03327 return retval > 0; 03328 } 03329 }; 03330 03331 #endif // SI_CONVERT_WIN32 03332 03333 03334 // --------------------------------------------------------------------------- 03335 // TYPE DEFINITIONS 03336 // --------------------------------------------------------------------------- 03337 03338 typedef CSimpleIniTempl<char, 03339 SI_NoCase<char>,SI_ConvertA<char> > CSimpleIniA; 03340 typedef CSimpleIniTempl<char, 03341 SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA; 03342 03343 #if defined(SI_CONVERT_ICU) 03344 typedef CSimpleIniTempl<UChar, 03345 SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW; 03346 typedef CSimpleIniTempl<UChar, 03347 SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW; 03348 #else 03349 typedef CSimpleIniTempl<wchar_t, 03350 SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW; 03351 typedef CSimpleIniTempl<wchar_t, 03352 SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW; 03353 #endif 03354 03355 #ifdef _UNICODE 03356 # define CSimpleIni CSimpleIniW 03357 # define CSimpleIniCase CSimpleIniCaseW 03358 # define SI_NEWLINE SI_NEWLINE_W 03359 #else // !_UNICODE 03360 # define CSimpleIni CSimpleIniA 03361 # define CSimpleIniCase CSimpleIniCaseA 03362 # define SI_NEWLINE SI_NEWLINE_A 03363 #endif // _UNICODE 03364 03365 #ifdef _MSC_VER 03366 # pragma warning (pop) 03367 #endif 03368 03369 #endif // INCLUDED_SimpleIni_h 03370
1.7.3