SimpleIni.h
Go to the documentation of this file.
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 = SI_CONVERTER::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 = &empty;
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 


rtabmap
Author(s): Mathieu Labbe
autogenerated on Thu Jun 6 2019 21:59:22