#include "IniFile.h" #include "StdString.h" #include #include CIniFileBase::CIniFileBase(CFileBase & FileObject, const char * FileName) : m_lastSectionSearch(0), m_CurrentSectionFilePos(0), m_LineFeed("\r\n"), m_ReadOnly(true), m_InstantFlush(true), m_File(FileObject), m_FileName(FileName), m_CurrentSectionDirty(false), m_SortFunction(nullptr) { } CIniFileBase::~CIniFileBase(void) { SaveCurrentSection(); } void CIniFileBase::fInsertSpaces(int Pos, int NoOfSpaces) { enum { fIS_MvSize = 0x2000 }; unsigned char Data[fIS_MvSize + 1]; int SizeToRead, result; long end, WritePos; m_File.Seek(0, CFileBase::end); end = (long)m_File.GetPosition(); if (NoOfSpaces > 0) { std::string SpaceBuffer = FormatStr("%*c", NoOfSpaces, ' '); do { SizeToRead = end - Pos; if (SizeToRead > fIS_MvSize) { SizeToRead = fIS_MvSize; } if (SizeToRead > 0) { m_File.Seek(SizeToRead * -1, CFileBase::current); WritePos = (long)m_File.GetPosition(); memset(Data, 0, sizeof(Data)); result = m_File.Read(Data, SizeToRead); m_File.Seek(WritePos, CFileBase::begin); end = WritePos; m_File.Write(SpaceBuffer.c_str(), (uint32_t)SpaceBuffer.length()); m_File.Write(Data, result); m_File.Seek(WritePos, CFileBase::begin); } } while (SizeToRead > 0); } if (NoOfSpaces < 0) { int ReadPos = Pos + (NoOfSpaces * -1); WritePos = Pos; do { SizeToRead = end - ReadPos; if (SizeToRead > fIS_MvSize) { SizeToRead = fIS_MvSize; } m_File.Seek(ReadPos, CFileBase::begin); m_File.Read(Data, SizeToRead); m_File.Seek(WritePos, CFileBase::begin); m_File.Write(Data, SizeToRead); ReadPos += SizeToRead; WritePos += SizeToRead; } while (SizeToRead > 0); m_File.Seek(WritePos, CFileBase::begin); std::string SpaceBuffer = FormatStr("%*c", (NoOfSpaces * -1), ' '); m_File.Write(SpaceBuffer.c_str(), (uint32_t)SpaceBuffer.length()); m_File.Seek(WritePos, CFileBase::begin); m_File.SetEndOfFile(); m_File.Seek(0, CFileBase::begin); } } int CIniFileBase::GetStringFromFile(char *& String, std::unique_ptr & Data, int & MaxDataSize, int & DataSize, int & ReadPos) { enum { BufferIncrease = 0x2000 }; if (MaxDataSize == 0) { ReadPos = 0; MaxDataSize = BufferIncrease; Data.reset(new char[MaxDataSize]); DataSize = m_File.Read(&Data.get()[DataSize], MaxDataSize); } for (;;) { int count; for (count = ReadPos; count < DataSize; count++) { if (Data.get()[count] == '\n') { int len = (count - ReadPos) + 1; String = &Data.get()[ReadPos]; String[len - 1] = 0; ReadPos = count + 1; return len; } } if (ReadPos != 0) { if ((DataSize - ReadPos) > 0) { memmove(Data.get(), &Data.get()[ReadPos], DataSize - ReadPos); } DataSize -= ReadPos; ReadPos = 0; } else { // Increase buffer size int NewMaxDataSize = MaxDataSize + BufferIncrease; char * NewBuffer = new char[NewMaxDataSize]; if (NewBuffer == nullptr) { return -1; } memcpy(NewBuffer, Data.get(), DataSize); MaxDataSize = NewMaxDataSize; Data.reset(NewBuffer); } int dwRead = m_File.Read(&Data.get()[DataSize], MaxDataSize - DataSize); if (dwRead == 0) { if (DataSize > 0) { int len = DataSize + 1; String = &Data.get()[ReadPos]; String[len - 1] = 0; DataSize = 0; ReadPos = 0; return len; } return -1; } DataSize += dwRead; } } void CIniFileBase::SaveCurrentSection(void) { if (!m_CurrentSectionDirty || m_ReadOnly) { return; } m_CurrentSectionDirty = false; if (m_CurrentSection.length() == 0) { m_CurrentSection = "default"; } int lineFeedLen = (int)strlen(m_LineFeed); if (m_CurrentSectionFilePos == -1) { // Section has not been added yet m_File.Seek(0, CFileBase::end); int len = (int)m_CurrentSection.length() + (lineFeedLen * 2) + 5; std::unique_ptr SectionName(new char[len]); if (m_File.GetLength() < (int)strlen(m_LineFeed)) { sprintf(SectionName.get(), "[%s]%s", m_CurrentSection.c_str(), m_LineFeed); } else { sprintf(SectionName.get(), "%s[%s]%s", m_LineFeed, m_CurrentSection.c_str(), m_LineFeed); } m_File.Write(SectionName.get(), (int)strlen(SectionName.get())); m_CurrentSectionFilePos = (long)m_File.GetPosition(); m_SectionsPos.insert(FILELOC::value_type(m_CurrentSection, m_CurrentSectionFilePos)); } else { // Increase/decrease space needed int NeededBufferLen = 0; { std::unique_ptr LineData; int len = 0; for (KeyValueList::iterator iter = m_CurrentSectionData.begin(); iter != m_CurrentSectionData.end(); iter++) { int newLen = (int)iter->first.length() + (int)iter->second.length() + lineFeedLen + 5; if (newLen > len) { LineData.reset(new char[newLen]); len = newLen; } sprintf(LineData.get(), "%s=%s%s", iter->first.c_str(), iter->second.c_str(), m_LineFeed); NeededBufferLen += (int)strlen(LineData.get()); } } int currentLen = 0; m_File.Seek(m_CurrentSectionFilePos, CFileBase::begin); int MaxDataSize = 0, DataSize = 0, ReadPos = 0, result; std::unique_ptr Data; char * Input = nullptr; // Skip first line as it is the section name int StartPos = m_CurrentSectionFilePos; int EndPos = StartPos; do { result = GetStringFromFile(Input, Data, MaxDataSize, DataSize, ReadPos); if (result <= 1) { continue; } if (strlen(CleanLine(Input)) <= 1 || Input[0] != '[') { EndPos = (long)((m_File.GetPosition() - DataSize) + ReadPos); continue; } if (Input[0] == '[') { NeededBufferLen += lineFeedLen; } break; } while (result >= 0); currentLen = EndPos - StartPos; if (NeededBufferLen != currentLen) { fInsertSpaces(StartPos, NeededBufferLen - currentLen); m_File.Flush(); ClearSectionPosList(StartPos); } // Set pointer to beginning of the start POS m_File.Seek(StartPos, CFileBase::begin); } { std::unique_ptr LineData; int len = 0; if (m_SortFunction != nullptr) { KeyValueVector data; for (KeyValueList::iterator iter = m_CurrentSectionData.begin(); iter != m_CurrentSectionData.end(); iter++) { data.push_back(KeyValueItem(&iter->first, &iter->second)); } m_SortFunction(data); for (size_t i = 0, n = data.size(); i < n; i++) { KeyValueItem & item = data[i]; int newLen = (int)(item.first->length()) + (int)item.second->length() + lineFeedLen + 5; if (newLen > len) { LineData.reset(new char[newLen]); len = newLen; } sprintf(LineData.get(), "%s=%s%s", item.first->c_str(), item.second->c_str(), m_LineFeed); m_File.Write(LineData.get(), (int)strlen(LineData.get())); } } else { for (KeyValueList::iterator iter = m_CurrentSectionData.begin(); iter != m_CurrentSectionData.end(); iter++) { int newLen = (int)iter->first.length() + (int)iter->second.length() + lineFeedLen + 5; if (newLen > len) { LineData.reset(new char[newLen]); len = newLen; } sprintf(LineData.get(), "%s=%s%s", iter->first.c_str(), iter->second.c_str(), m_LineFeed); m_File.Write(LineData.get(), (int)strlen(LineData.get())); } } } m_File.Flush(); } bool CIniFileBase::MoveToSectionNameData(const char * lpSectionName, bool ChangeCurrentSection) { if (strcmp(lpSectionName, m_CurrentSection.c_str()) == 0) { return true; } if (ChangeCurrentSection) { SaveCurrentSection(); m_CurrentSection = ""; } std::unique_ptr Data; char * Input = nullptr; int MaxDataSize = 0, DataSize = 0, ReadPos = 0, result; FILELOC_ITR iter = m_SectionsPos.find(std::string(lpSectionName)); bool bFoundSection = false; if (iter != m_SectionsPos.end()) { if (ChangeCurrentSection) { m_CurrentSection = iter->first; m_CurrentSectionFilePos = iter->second; } m_File.Seek(iter->second, CFileBase::begin); bFoundSection = true; } else { m_File.Seek(m_lastSectionSearch, CFileBase::begin); //long Fpos; uint8_t pUTF8[3]; pUTF8[0] = 0xef; pUTF8[1] = 0xbb; pUTF8[2] = 0xbf; do { result = GetStringFromFile(Input, Data, MaxDataSize, DataSize, ReadPos); if (result <= 1) { continue; } if (strlen(CleanLine(Input)) <= 1) { continue; } // We only care about sections char * CurrentSection = Input; if (m_lastSectionSearch == 0 && !memcmp(CurrentSection, pUTF8, 3)) { CurrentSection += 3; } if (CurrentSection[0] != '[') { continue; } int lineEndPos = (int)strlen(CurrentSection) - 1; if (CurrentSection[lineEndPos] != ']') { continue; } // Take off the ']' from the end of the string CurrentSection[lineEndPos] = 0; CurrentSection += 1; m_lastSectionSearch = (long)((m_File.GetPosition() - DataSize) + ReadPos); m_SectionsPos.insert(FILELOC::value_type(CurrentSection, m_lastSectionSearch)); if (_stricmp(lpSectionName, CurrentSection) != 0) { continue; } if (ChangeCurrentSection) { m_CurrentSection = lpSectionName; m_CurrentSectionFilePos = m_lastSectionSearch; } else { m_File.Seek(m_lastSectionSearch, CFileBase::begin); } bFoundSection = true; break; } while (result >= 0); } if (!bFoundSection && strcmp(lpSectionName, "default") == 0) { m_SectionsPos.insert(FILELOC::value_type(lpSectionName, 0)); if (ChangeCurrentSection) { m_CurrentSection = lpSectionName; m_CurrentSectionFilePos = 0; } m_File.Seek(m_lastSectionSearch, CFileBase::begin); bFoundSection = true; } if (bFoundSection && ChangeCurrentSection) { m_CurrentSectionData.clear(); do { result = GetStringFromFile(Input, Data, MaxDataSize, DataSize, ReadPos); if (result <= 1) { continue; } if (strlen(CleanLine(Input)) <= 1) { continue; } if (Input[0] == '[') { break; } char * Pos = strchr(Input, '='); if (Pos == nullptr) { continue; } char * Value = &Pos[1]; char * Pos1 = Pos - 1; while (((*Pos1 == ' ') || (*Pos1 == '\t')) && (Pos1 > Input)) { Pos1--; } Pos1[1] = 0; m_CurrentSectionData.insert(KeyValueList::value_type(Input, Value)); } while (result >= 0); } return bFoundSection; } const char * CIniFileBase::CleanLine(char * Line) { char * Pos = Line; // Remove any comment from the line while (Pos != nullptr) { Pos = strchr(Pos, '/'); if (Pos != nullptr) { if (Pos[1] == '/') { if (Pos > Line) { char * Pos_1 = Pos - 1; if (Pos_1[0] != ':') { Pos[0] = 0; } else Pos += 1; } else { Pos[0] = 0; } } else { Pos += 1; } } } // Strip any spaces or line feeds from the end of the line for (int count = (int)strlen(&Line[0]) - 1; count >= 0; count--) { if (Line[count] != ' ' && Line[count] != '\r') { break; } Line[count] = 0; } return Line; } void CIniFileBase::OpenIniFileReadOnly() { if (m_File.Open(m_FileName.c_str(), CFileBase::modeRead)) { m_ReadOnly = true; m_File.Seek(0, CFileBase::begin); } } void CIniFileBase::OpenIniFile(bool bCreate) { // Open for reading/writing m_ReadOnly = false; if (!m_File.Open(m_FileName.c_str(), CFileBase::modeReadWrite | CFileBase::shareDenyWrite)) { if (!m_File.Open(m_FileName.c_str(), CFileBase::modeRead)) { if (bCreate) { if (!m_File.Open(m_FileName.c_str(), CFileBase::modeReadWrite | CFileBase::modeCreate | CFileBase::shareDenyWrite)) { return; } } } else { m_ReadOnly = true; } } m_File.Seek(0, CFileBase::begin); } bool CIniFileBase::IsEmpty() { if (m_File.GetLength() == 0) return true; return false; } bool CIniFileBase::IsFileOpen(void) { return m_File.IsOpen(); } bool CIniFileBase::IsReadOnly(void) { return m_ReadOnly; } bool CIniFileBase::DeleteSection(const char * lpSectionName) { CGuard Guard(m_CS); if (!m_File.IsOpen()) { return false; } SaveCurrentSection(); if (!MoveToSectionNameData(lpSectionName, true)) { return false; } m_File.Seek(m_CurrentSectionFilePos, CFileBase::begin); long DeleteSectionStart = (long)(m_CurrentSectionFilePos - (strlen(lpSectionName) + strlen(m_LineFeed) + 2)); long NextSectionStart = -1; { int MaxDataSize = 0, DataSize = 0, ReadPos = 0, NextLine = 0, result; std::unique_ptr Data; char * Input = nullptr; do { result = GetStringFromFile(Input, Data, MaxDataSize, DataSize, ReadPos); if (result <= 1) { continue; } if (strlen(CleanLine(Input)) <= 1) { continue; } if (Input[0] != '[') { NextLine = (int)((m_File.GetPosition() - DataSize) + ReadPos); continue; } NextSectionStart = NextLine != 0 ? NextLine : (long)((m_File.GetPosition() - DataSize) + ReadPos); break; } while (result >= 0); } if (NextSectionStart != -1) { m_File.Seek(0, CFileBase::end); long end = (long)m_File.GetPosition(); long ReadPos = NextSectionStart; long WritePos = DeleteSectionStart; enum { fIS_MvSize = 0x2000 }; unsigned char Data[fIS_MvSize + 1]; int SizeToRead; do { SizeToRead = end - ReadPos; if (SizeToRead > fIS_MvSize) { SizeToRead = fIS_MvSize; } m_File.Seek(ReadPos, CFileBase::begin); m_File.Read(Data, SizeToRead); m_File.Seek(WritePos, CFileBase::begin); m_File.Write(Data, SizeToRead); ReadPos += SizeToRead; WritePos += SizeToRead; } while (SizeToRead > 0); m_File.Seek(DeleteSectionStart + (end - NextSectionStart), CFileBase::begin); m_File.Flush(); m_File.SetEndOfFile(); } else { m_File.Seek(DeleteSectionStart, CFileBase::begin); m_File.Flush(); m_File.SetEndOfFile(); } m_File.Flush(); ClearSectionPosList(0); m_CurrentSection = ""; m_CurrentSectionData.clear(); m_CurrentSectionFilePos = -1; return true; } bool CIniFileBase::GetString(const char * lpSectionName, const char * lpKeyName, const char * lpDefault, std::string & Value) { CGuard Guard(m_CS); if (lpSectionName == nullptr || strlen(lpSectionName) == 0) { lpSectionName = "default"; } if (m_File.IsOpen() && MoveToSectionNameData(lpSectionName, true)) { KeyValueList::iterator iter = m_CurrentSectionData.find(lpKeyName); if (iter != m_CurrentSectionData.end()) { Value = iter->second.c_str(); return true; } } Value = lpDefault; return false; } std::string CIniFileBase::GetString(const char * lpSectionName, const char * lpKeyName, const char * lpDefault) { std::string Value; GetString(lpSectionName, lpKeyName, lpDefault, Value); return Value; } uint32_t CIniFileBase::GetString(const char * lpSectionName, const char * lpKeyName, const char * lpDefault, char * lpReturnedString, uint32_t nSize) { CGuard Guard(m_CS); std::string strSection; if (lpSectionName == nullptr || strlen(lpSectionName) == 0) { strSection = "default"; } else { strSection = lpSectionName; } if (m_File.IsOpen() && MoveToSectionNameData(strSection.c_str(), true)) { KeyValueList::iterator iter = m_CurrentSectionData.find(lpKeyName); if (iter != m_CurrentSectionData.end()) { strncpy(lpReturnedString, iter->second.c_str(), nSize - 1); lpReturnedString[nSize - 1] = 0; return (uint32_t)strlen(lpReturnedString); } } strncpy(lpReturnedString, lpDefault, nSize - 1); lpReturnedString[nSize - 1] = 0; return (uint32_t)strlen(lpReturnedString); } uint32_t CIniFileBase::GetNumber(const char * lpSectionName, const char * lpKeyName, uint32_t nDefault) { uint32_t Value; GetNumber(lpSectionName, lpKeyName, nDefault, Value); return Value; } bool CIniFileBase::GetNumber(const char * lpSectionName, const char * lpKeyName, uint32_t nDefault, uint32_t & Value) { CGuard Guard(m_CS); if (lpSectionName == nullptr || strlen(lpSectionName) == 0) { lpSectionName = "default"; } if (m_File.IsOpen() && MoveToSectionNameData(lpSectionName, true)) { KeyValueList::iterator iter = m_CurrentSectionData.find(lpKeyName); if (iter != m_CurrentSectionData.end()) { Value = 0; sscanf(iter->second.c_str(), "%u", &Value); return true; } } Value = nDefault; return false; } void CIniFileBase::SaveString(const char * lpSectionName, const char * lpKeyName, const char * lpString) { if (m_ReadOnly) { return; } CGuard Guard(m_CS); if (!m_File.IsOpen()) { if (lpString) { OpenIniFile(); } if (!m_File.IsOpen()) { return; } } std::string strSection; if (lpSectionName == nullptr || strlen(lpSectionName) == 0) { strSection = "default"; } else { strSection = lpSectionName; } if (!MoveToSectionNameData(strSection.c_str(), true)) { m_CurrentSection = strSection; m_CurrentSectionData.clear(); m_CurrentSectionFilePos = -1; } KeyValueList::iterator iter = m_CurrentSectionData.find(lpKeyName); if (iter != m_CurrentSectionData.end()) { if (lpString) { if (iter->second != lpString) { iter->second = lpString; m_CurrentSectionDirty = true; } } else { m_CurrentSectionData.erase(iter); m_CurrentSectionDirty = true; } } else { if (lpString) { m_CurrentSectionData.insert(KeyValueList::value_type(lpKeyName, lpString)); m_CurrentSectionDirty = true; } } if (m_InstantFlush) { SaveCurrentSection(); } } void CIniFileBase::SaveNumber(const char * lpSectionName, const char * lpKeyName, uint32_t Value) { if (m_ReadOnly) { return; } // Translate the string to an ASCII version and save as text SaveString(lpSectionName, lpKeyName, FormatStr("%d", Value).c_str()); } bool CIniFileBase::EntryExists(const char * lpSectionName, const char * lpKeyName) { CGuard Guard(m_CS); if (lpSectionName == nullptr || strlen(lpSectionName) == 0) { lpSectionName = "default"; } if (m_File.IsOpen() && MoveToSectionNameData(lpSectionName, true)) { KeyValueList::iterator iter = m_CurrentSectionData.find(lpKeyName); if (iter != m_CurrentSectionData.end()) { return true; } } return false; } void CIniFileBase::FlushChanges(void) { CGuard Guard(m_CS); SaveCurrentSection(); } void CIniFileBase::SetAutoFlush(bool AutoFlush) { m_InstantFlush = AutoFlush; if (AutoFlush) { FlushChanges(); } } void CIniFileBase::GetKeyList(const char * lpSectionName, strlist & List) { List.clear(); CGuard Guard(m_CS); if (!m_File.IsOpen()) { return; } if (lpSectionName == nullptr || strlen(lpSectionName) == 0) { lpSectionName = "default"; } if (MoveToSectionNameData(lpSectionName, true)) { for (KeyValueList::iterator iter = m_CurrentSectionData.begin(); iter != m_CurrentSectionData.end(); iter++) { List.push_back(iter->first); } } } void CIniFileBase::GetKeyValueData(const char * lpSectionName, KeyValueData & List) { CGuard Guard(m_CS); if (!m_File.IsOpen()) { return; } std::string strSection; if (lpSectionName == nullptr || strlen(lpSectionName) == 0) { strSection = "default"; } else { strSection = lpSectionName; } if (!MoveToSectionNameData(strSection.c_str(), false)) { return; } int MaxDataSize = 0, DataSize = 0, ReadPos = 0, result; std::unique_ptr Data; char * Input = nullptr; do { result = GetStringFromFile(Input, Data, MaxDataSize, DataSize, ReadPos); if (result <= 1) { continue; } if (strlen(CleanLine(Input)) <= 1) { continue; } if (Input[0] == '[') { break; } char * Pos = strchr(Input, '='); if (Pos == nullptr) { continue; } Pos[0] = 0; List.insert(KeyValueData::value_type(stdstr(Input).Trim(), &Pos[1])); } while (result >= 0); } void CIniFileBase::SetCustomSort(SortData SortFunction) { CGuard Guard(m_CS); m_SortFunction = SortFunction; } void CIniFileBase::ClearSectionPosList(long FilePos) { if (FilePos <= 0) { m_SectionsPos.clear(); m_lastSectionSearch = 0; } else { FILELOC::iterator iter = m_SectionsPos.begin(); while (iter != m_SectionsPos.end()) { FILELOC::iterator CurrentIter = iter; iter++; long TestFilePos = CurrentIter->second; if (TestFilePos > FilePos) { m_SectionsPos.erase(CurrentIter); } } m_lastSectionSearch = FilePos; } } void CIniFileBase::GetVectorOfSections(SectionList & sections) { sections.clear(); CGuard Guard(m_CS); if (!m_File.IsOpen()) { return; } { std::string DoesNotExist = FormatStr("DoesNotExist%d%d%d", rand(), rand(), rand()); MoveToSectionNameData(DoesNotExist.c_str(), false); } for (FILELOC::const_iterator iter = m_SectionsPos.begin(); iter != m_SectionsPos.end(); iter++) { sections.insert(iter->first); } } std::string CIniFileBase::FormatStr(const char * strFormat, ...) { std::string FormatedStr; va_list args; va_start(args, strFormat); size_t nlen = _vscprintf(strFormat, args) + 1; char * buffer = (char *)alloca(nlen * sizeof(char)); buffer[nlen - 1] = 0; if (buffer != nullptr) { vsprintf(buffer, strFormat, args); FormatedStr = buffer; } va_end(args); return FormatedStr; }