961 lines
22 KiB
C++
961 lines
22 KiB
C++
/*
|
|
* MIT License
|
|
*
|
|
* Copyright (c) 2023 dujingning <djn2019x@163.com> <https://github.com/dujingning/inicpp.git>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#ifndef __JN_INICPP_H__
|
|
#define __JN_INICPP_H__
|
|
|
|
#include <cstddef>
|
|
#include <stdexcept>
|
|
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <list>
|
|
#include <map>
|
|
|
|
#ifdef _ENBABLE_INICPP_STD_WSTRING_ // Not all of C++ 11 support <codecvt>
|
|
// for std::string <==> std::wstring convert
|
|
#include <codecvt>
|
|
#include <locale>
|
|
#endif
|
|
|
|
#ifdef INICPP_DEBUG
|
|
|
|
#include <array>
|
|
#include <ctime>
|
|
#include <iostream>
|
|
|
|
class TimeFormatter
|
|
{
|
|
public:
|
|
static std::string format(const std::string &format = "%Y-%m-%d %H:%M:%S")
|
|
{
|
|
std::time_t t = std::time(nullptr);
|
|
std::tm tm = *std::localtime(&t);
|
|
std::array<char, 100> buffer;
|
|
std::strftime(buffer.data(), buffer.size(), format.c_str(), &tm);
|
|
return buffer.data();
|
|
}
|
|
};
|
|
|
|
#define CODE_INFO std::string(" \t``````|") + std::string(__FILE__) + ":" + std::to_string(__LINE__) + " fun:" + std::string(__FUNCTION__)
|
|
#define INI_DEBUG(x) std::cout << "INICPP " << TimeFormatter::format() << " : " << x << CODE_INFO << std::endl
|
|
|
|
#else // #ifdef INICPP_DEBUG
|
|
#define INI_DEBUG(x)
|
|
#endif // #ifdef INICPP_DEBUG
|
|
|
|
namespace inicpp
|
|
{
|
|
|
|
typedef struct ValueNode
|
|
{
|
|
std::string Value = "";
|
|
int lineNumber = -1; // text line start with 1
|
|
} ValueNode;
|
|
|
|
class parentHelper
|
|
{
|
|
public:
|
|
virtual parentHelper *parent()
|
|
{
|
|
INI_DEBUG("called parentHelper virtual impl: need to impl parent");
|
|
return nullptr;
|
|
}
|
|
virtual void setParent(parentHelper *parent)
|
|
{
|
|
INI_DEBUG("called parentHelper virtual impl: need to impl setParent");
|
|
}
|
|
virtual bool set(const std::string &Section, const std::string &Key, const std::string &Value, const std::string &comment = "")
|
|
{
|
|
INI_DEBUG("called parentHelper virtual impl: need to impl set");
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class ValueProxy
|
|
{
|
|
public:
|
|
ValueProxy(std::string &value) : _value(value) {}
|
|
~ValueProxy() {}
|
|
|
|
template <typename T>
|
|
ValueProxy(const T &value) : _value(to_string(value)) {}
|
|
|
|
template <typename T>
|
|
static std::string to_string(const T &value)
|
|
{
|
|
std::ostringstream oss;
|
|
oss << value;
|
|
return oss.str();
|
|
}
|
|
|
|
template <typename T>
|
|
T get() const
|
|
{
|
|
static_assert(!std::is_pointer<T>::value, "Pointer types are not supported for conversion.");
|
|
std::istringstream iss(_value);
|
|
T result;
|
|
if (!(iss >> result))
|
|
{
|
|
throw std::runtime_error("Type mismatch or invalid conversion. with(section-key-value): " +_sectionName +"-"+ _keyName +"-"+ _value); // error notify
|
|
}
|
|
return result;
|
|
}
|
|
|
|
operator char() const { return this->get<char>(); }
|
|
operator short() const { return this->get<short>(); }
|
|
operator int() const { return this->get<int>(); }
|
|
operator long() const { return this->get<long>(); }
|
|
operator long long() const { return this->get<long long>(); }
|
|
operator float() const { return this->get<float>(); }
|
|
operator double() const { return this->get<double>(); }
|
|
|
|
operator unsigned char() const { return this->get<unsigned char>(); }
|
|
operator unsigned short() const { return this->get<unsigned short>(); }
|
|
operator unsigned int() const { return this->get<unsigned int>(); }
|
|
operator unsigned long() const { return this->get<unsigned long>(); }
|
|
operator unsigned long long() const { return this->get<unsigned long long>(); }
|
|
|
|
// false:'0' or 'false', true : others
|
|
operator bool() const
|
|
{
|
|
if (_value == "0" || _value == "false" || _value == "no")
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
operator std::string() const
|
|
{
|
|
return _value;
|
|
}
|
|
|
|
friend std::ostream &operator<<(std::ostream &os, const ValueProxy &proxy)
|
|
{
|
|
os << proxy._value;
|
|
return os;
|
|
}
|
|
|
|
// assignment
|
|
template <typename T>
|
|
ValueProxy &operator=(const T &other)
|
|
{
|
|
std::string value = this->to_string(other);
|
|
|
|
if (_value != value)
|
|
{
|
|
set(value);
|
|
}
|
|
|
|
_value = value;
|
|
return *this;
|
|
}
|
|
|
|
ValueProxy &operator=(const std::string &other)
|
|
{
|
|
if (_value != other)
|
|
{
|
|
INI_DEBUG("Value Proxy Wanna Set Value: " << other);
|
|
set(other);
|
|
}
|
|
|
|
_value = other;
|
|
return *this;
|
|
}
|
|
|
|
// specify std::string
|
|
const std::string &String() noexcept
|
|
{
|
|
return _value;
|
|
}
|
|
|
|
inline void setWriteCB(parentHelper *sectionObj, const std::string §ionName, const std::string &keyName)
|
|
{
|
|
_section = sectionObj;
|
|
_sectionName = sectionName;
|
|
_keyName = keyName;
|
|
}
|
|
|
|
private:
|
|
void set(const std::string &value)
|
|
{
|
|
if (value.empty() || _keyName.empty())
|
|
{
|
|
return;
|
|
}
|
|
if (_section && _section->parent() && _section->parent()->parent())
|
|
{
|
|
_section->parent()->parent()->set(_sectionName, _keyName, value);
|
|
}
|
|
}
|
|
|
|
private:
|
|
std::string &_value;
|
|
|
|
std::string _sectionName, _keyName;
|
|
parentHelper *_section = nullptr;
|
|
};
|
|
} // namespace inicpp
|
|
|
|
namespace inicpp
|
|
{
|
|
|
|
class section : parentHelper
|
|
{
|
|
public:
|
|
section() : _sectionName()
|
|
{
|
|
}
|
|
|
|
explicit section(const std::string §ionName) : _sectionName(sectionName)
|
|
{
|
|
}
|
|
|
|
const std::string &name()
|
|
{
|
|
return _sectionName;
|
|
}
|
|
|
|
const std::string getValue(const std::string &Key)
|
|
{
|
|
if (!_sectionMap.count(Key))
|
|
{
|
|
return "";
|
|
}
|
|
return _sectionMap[Key].Value;
|
|
}
|
|
|
|
void setName(const std::string &name, const int &lineNumber)
|
|
{
|
|
_sectionName = name;
|
|
_lineNumber = lineNumber;
|
|
}
|
|
|
|
void setValue(const std::string &Key, const std::string &Value, const int line)
|
|
{
|
|
_sectionMap[Key].Value = Value;
|
|
_sectionMap[Key].lineNumber = line;
|
|
}
|
|
|
|
void append(section &sec)
|
|
{
|
|
_sectionMap.insert(sec._sectionMap.begin(), sec._sectionMap.end());
|
|
}
|
|
|
|
bool isKeyExist(const std::string &Key)
|
|
{
|
|
return !_sectionMap.count(Key) ? false : true;
|
|
}
|
|
|
|
int getEndSection()
|
|
{
|
|
int line = -1;
|
|
|
|
if (_sectionMap.empty() && _sectionName != "")
|
|
{
|
|
return _lineNumber;
|
|
}
|
|
|
|
for (const auto &data : _sectionMap)
|
|
{
|
|
if (data.second.lineNumber > line)
|
|
{
|
|
line = data.second.lineNumber;
|
|
}
|
|
}
|
|
return line;
|
|
}
|
|
|
|
int getLine(const std::string &Key)
|
|
{
|
|
if (!_sectionMap.count(Key))
|
|
{
|
|
return -1;
|
|
}
|
|
return _sectionMap[Key].lineNumber;
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
_lineNumber = -1;
|
|
_sectionName.clear();
|
|
_sectionMap.clear();
|
|
}
|
|
|
|
bool isEmpty() const
|
|
{
|
|
return _sectionMap.empty();
|
|
}
|
|
|
|
int toInt(const std::string &Key) noexcept
|
|
{
|
|
if (!_sectionMap.count(Key))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int result = 0;
|
|
|
|
try
|
|
{
|
|
result = std::stoi(_sectionMap[Key].Value);
|
|
}
|
|
catch (const std::invalid_argument &e)
|
|
{
|
|
INI_DEBUG("Invalid argument: " << e.what() << ",input:\'" << _sectionMap[Key].Value << "\'");
|
|
}
|
|
catch (const std::out_of_range &e)
|
|
{
|
|
INI_DEBUG("Out of range: " << e.what() << ",input:\'" << _sectionMap[Key].Value << "\'");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::string toString(const std::string &Key) noexcept
|
|
{
|
|
if (!_sectionMap.count(Key))
|
|
{
|
|
return "";
|
|
}
|
|
return _sectionMap[Key].Value;
|
|
}
|
|
|
|
#ifdef _ENBABLE_INICPP_STD_WSTRING_
|
|
std::wstring toWString(const std::string &Key)
|
|
{
|
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
|
return converter.from_bytes(toString(Key));
|
|
}
|
|
#endif
|
|
|
|
double toDouble(const std::string &Key) noexcept
|
|
{
|
|
if (!_sectionMap.count(Key))
|
|
{
|
|
return 0.0;
|
|
}
|
|
|
|
double result = 0.0;
|
|
|
|
try
|
|
{
|
|
result = std::stod(_sectionMap[Key].Value);
|
|
}
|
|
catch (const std::invalid_argument &e)
|
|
{
|
|
INI_DEBUG("Invalid argument: " << e.what() << ",input:\'" << _sectionMap[Key].Value << "\'");
|
|
}
|
|
catch (const std::out_of_range &e)
|
|
{
|
|
INI_DEBUG("Out of range: " << e.what() << ",input:\'" << _sectionMap[Key].Value << "\'");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::map<std::string /*Key*/, std::string /*Value*/> getSectionMap()
|
|
{
|
|
std::map<std::string /*Key*/, std::string /*Value*/> sectionKVMap;
|
|
|
|
for (auto &iter : _sectionMap)
|
|
{
|
|
sectionKVMap[iter.first] = iter.second.Value;
|
|
}
|
|
|
|
return sectionKVMap;
|
|
}
|
|
|
|
// Automatically converts to any type; throws std::runtime_error if not found or conversion fails
|
|
ValueProxy operator[](const std::string &Key)
|
|
{
|
|
ValueProxy vp(_sectionMap[Key].Value);
|
|
|
|
vp.setWriteCB(this, _sectionName, Key);
|
|
|
|
return vp;
|
|
}
|
|
|
|
inline parentHelper *parent() override { return _parent; };
|
|
inline void setParent(parentHelper *parent) override { _parent = parent; };
|
|
|
|
private:
|
|
std::string _sectionName;
|
|
std::map<std::string /*Key*/, ValueNode> _sectionMap;
|
|
int _lineNumber = -1; // text line start with 1
|
|
|
|
parentHelper *_parent = nullptr;
|
|
};
|
|
|
|
class ini : parentHelper
|
|
{
|
|
public:
|
|
void addSection(section &sec)
|
|
{
|
|
if (_iniInfoMap.count(sec.name())) // if exist,need to merge
|
|
{
|
|
_iniInfoMap[sec.name()].append(sec);
|
|
return;
|
|
}
|
|
_iniInfoMap.emplace(sec.name(), sec);
|
|
return;
|
|
}
|
|
|
|
void removeSection(const std::string §ionName)
|
|
{
|
|
if (!_iniInfoMap.count(sectionName))
|
|
{
|
|
return;
|
|
}
|
|
_iniInfoMap.erase(sectionName);
|
|
return;
|
|
}
|
|
|
|
bool isSectionExists(const std::string §ionName)
|
|
{
|
|
return !_iniInfoMap.count(sectionName) ? false : true;
|
|
}
|
|
|
|
// may contains default of Unnamed section with ""
|
|
std::list<std::string> getSectionsList()
|
|
{
|
|
std::list<std::string> sectionList;
|
|
for (const auto &data : _iniInfoMap)
|
|
{
|
|
if (data.first == "" && data.second.isEmpty()) // default section: no section name,if empty,not count it.
|
|
{
|
|
continue;
|
|
}
|
|
sectionList.emplace_back(data.first);
|
|
}
|
|
return sectionList;
|
|
}
|
|
|
|
std::map<std::string /*key*/, std::string /*value*/> getSectionMap(const std::string §ionName)
|
|
{
|
|
std::map<std::string /*key*/, std::string /*value*/> kvMap;
|
|
if (_iniInfoMap.count(sectionName) == 0)
|
|
{
|
|
return kvMap;
|
|
}
|
|
return _iniInfoMap[sectionName].getSectionMap();
|
|
}
|
|
|
|
const section &operator[](const std::string §ionName)
|
|
{
|
|
_iniInfoMap[sectionName].setParent(this);
|
|
|
|
if (_iniInfoMap[sectionName].name().empty())
|
|
{
|
|
_iniInfoMap[sectionName].setName(sectionName, -1);
|
|
}
|
|
|
|
return _iniInfoMap[sectionName];
|
|
}
|
|
|
|
inline std::size_t getSectionSize()
|
|
{
|
|
return _iniInfoMap.size();
|
|
}
|
|
|
|
std::string getValue(const std::string §ionName, const std::string &Key)
|
|
{
|
|
if (!_iniInfoMap.count(sectionName))
|
|
{
|
|
return "";
|
|
}
|
|
return _iniInfoMap[sectionName][Key];
|
|
}
|
|
|
|
// for none section
|
|
int getLine(const std::string &Key)
|
|
{
|
|
if (!_iniInfoMap.count(""))
|
|
{
|
|
return -1;
|
|
}
|
|
return _iniInfoMap[""].getLine(Key);
|
|
}
|
|
|
|
// for section-key
|
|
int getLine(const std::string §ionName, const std::string &Key)
|
|
{
|
|
if (!_iniInfoMap.count(sectionName))
|
|
{
|
|
return -1;
|
|
}
|
|
return _iniInfoMap[sectionName].getLine(Key);
|
|
}
|
|
|
|
inline void clear() { _iniInfoMap.clear(); }
|
|
inline bool empty() { return _iniInfoMap.empty(); }
|
|
|
|
parentHelper *parent() override { return _parent; }
|
|
void setParent(parentHelper *parent) override { _parent = parent; }
|
|
|
|
protected:
|
|
std::map<std::string /*Section Name*/, section> _iniInfoMap;
|
|
|
|
private:
|
|
parentHelper *_parent = nullptr;
|
|
};
|
|
|
|
class IniManager : parentHelper
|
|
{
|
|
public:
|
|
#ifdef _ENBABLE_INICPP_STD_WSTRING_
|
|
explicit IniManager(const std::wstring &configFileName = L"")
|
|
{
|
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
|
std::string stringValue = converter.to_bytes(configFileName);
|
|
_configFileName = stringValue;
|
|
|
|
_iniData.setParent(this);
|
|
parse();
|
|
}
|
|
#else
|
|
explicit IniManager(const std::string &configFileName = "") : _configFileName(configFileName)
|
|
{
|
|
_iniData.setParent(this);
|
|
|
|
parse();
|
|
}
|
|
#endif
|
|
~IniManager()
|
|
{
|
|
_iniFile.close();
|
|
}
|
|
|
|
section operator[](const std::string §ionName)
|
|
{
|
|
return _iniData[sectionName];
|
|
}
|
|
|
|
void parse()
|
|
{
|
|
if (_configFileName.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!_iniFile.is_open())
|
|
{
|
|
_iniFile.clear();
|
|
_iniFile.open(_configFileName, std::ifstream::in | std::ifstream::out | std::fstream::app);
|
|
}
|
|
|
|
if (!_iniFile.is_open())
|
|
{
|
|
INI_DEBUG("Failed to open(WR),try to open with readonly(R).");
|
|
_iniFile.clear();
|
|
_iniFile.open(_configFileName, std::ifstream::in);
|
|
}
|
|
|
|
if (!_iniFile.is_open())
|
|
{
|
|
INI_DEBUG("Failed to open the input INI file for parsing! file:" << _configFileName);
|
|
return;
|
|
}
|
|
|
|
_iniData.clear();
|
|
|
|
_iniFile.seekg(0, _iniFile.beg);
|
|
std::string data, sectionName;
|
|
int sectionLine = -1;
|
|
|
|
section sectionRecord;
|
|
|
|
_SumOfLines = 1;
|
|
do
|
|
{
|
|
std::getline(_iniFile, data);
|
|
|
|
if (!filterData(data))
|
|
{
|
|
++_SumOfLines;
|
|
continue;
|
|
}
|
|
|
|
if (data.find('[') == 0) // section
|
|
{
|
|
if (!sectionRecord.isEmpty() || sectionRecord.name() != "")
|
|
{
|
|
_iniData.addSection(sectionRecord);
|
|
}
|
|
|
|
size_t first = data.find('[');
|
|
size_t last = data.find(']');
|
|
|
|
if (last == std::string::npos)
|
|
{
|
|
++_SumOfLines;
|
|
continue;
|
|
}
|
|
|
|
sectionName = data.substr(first + 1, last - first - 1);
|
|
sectionLine = _SumOfLines;
|
|
|
|
sectionRecord.clear();
|
|
sectionRecord.setName(sectionName, sectionLine);
|
|
}
|
|
|
|
size_t pos = data.find('=');
|
|
if (pos != std::string::npos)
|
|
{ // k=v
|
|
std::string key = data.substr(0, pos);
|
|
std::string value = data.substr(pos + 1);
|
|
|
|
trimEdges(key);
|
|
trimEdges(value);
|
|
|
|
sectionRecord.setValue(key, value, _SumOfLines);
|
|
}
|
|
|
|
++_SumOfLines;
|
|
|
|
} while (!_iniFile.eof());
|
|
|
|
if (!sectionRecord.isEmpty())
|
|
{
|
|
sectionRecord.setName(sectionName, -1);
|
|
_iniData.addSection(sectionRecord);
|
|
}
|
|
|
|
if (_iniFile.is_open())
|
|
{
|
|
_iniFile.close();
|
|
}
|
|
}
|
|
|
|
bool set(const std::string &Section, const std::string &Key, const std::string &Value, const std::string &comment = "") override
|
|
{
|
|
parse();
|
|
|
|
std::string key = Key, value = Value;
|
|
|
|
trimEdges(key);
|
|
trimEdges(key);
|
|
|
|
if (key == "" || value == "")
|
|
{
|
|
INI_DEBUG("Invalid parameter input: key[" << key << "],value[" << value << "]");
|
|
return false;
|
|
}
|
|
|
|
std::string keyValueData = key + "=" + value + "\n";
|
|
if (comment.length() > 0)
|
|
{
|
|
keyValueData = comment + "\n" + keyValueData;
|
|
if (comment[0] != ';')
|
|
{
|
|
keyValueData = ";" + keyValueData;
|
|
}
|
|
}
|
|
|
|
const std::string &tempFile = ".temp.ini";
|
|
std::fstream input(_configFileName, std::ifstream::in);
|
|
std::ofstream output(tempFile);
|
|
|
|
if (!input.is_open())
|
|
{
|
|
INI_DEBUG("Failed to open the input INI file for modification! File name:" << _configFileName);
|
|
return false;
|
|
}
|
|
|
|
if (!output.is_open())
|
|
{
|
|
INI_DEBUG("Failed to open the output INI file for modification!");
|
|
return false;
|
|
}
|
|
|
|
int line_number_mark = -1;
|
|
bool isInputDataWited = false;
|
|
|
|
do
|
|
{
|
|
// exist key at one section replace it, or need to create it
|
|
if (_iniData.isSectionExists(Section))
|
|
{
|
|
line_number_mark = (*this)[Section].getLine(key);
|
|
|
|
if (line_number_mark == -1)
|
|
{ // section exist, key not exist
|
|
line_number_mark = (*this)[Section].getEndSection();
|
|
|
|
std::string lineData;
|
|
int input_line_number = 0;
|
|
while (std::getline(input, lineData))
|
|
{
|
|
++input_line_number;
|
|
|
|
if (input_line_number == (line_number_mark + 1))
|
|
{ // new line,append to next line
|
|
isInputDataWited = true;
|
|
output << keyValueData;
|
|
}
|
|
|
|
output << lineData << "\n";
|
|
}
|
|
|
|
if (input.eof() && !isInputDataWited)
|
|
{
|
|
isInputDataWited = true;
|
|
output << keyValueData;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (line_number_mark <= 0) // not found key at config file
|
|
{
|
|
input.seekg(0, input.beg);
|
|
|
|
bool isHoldSection = false;
|
|
std::string newLine = "\n\n";
|
|
if (Section != "" && Section.find("[") == std::string::npos && Section.find("]") == std::string::npos && Section.find("=") == std::string::npos)
|
|
{
|
|
if (_iniData.empty() || _iniData.getSectionSize() <= 0)
|
|
{
|
|
newLine.clear();
|
|
}
|
|
|
|
isHoldSection = true;
|
|
}
|
|
|
|
// 1.section is exist or empty section
|
|
if (_iniData.isSectionExists(Section) || Section == "")
|
|
{
|
|
// write key/value to head
|
|
if (isHoldSection)
|
|
{
|
|
output << newLine << "[" << Section << "]" << "\n";
|
|
}
|
|
output << keyValueData;
|
|
// write others
|
|
std::string lineData;
|
|
while (std::getline(input, lineData))
|
|
{
|
|
output << lineData << "\n";
|
|
}
|
|
}
|
|
// 2.section is not exist
|
|
else
|
|
{
|
|
// write others
|
|
std::string lineData;
|
|
while (std::getline(input, lineData))
|
|
{
|
|
output << lineData << "\n";
|
|
}
|
|
// write key/value to end
|
|
if (isHoldSection)
|
|
{
|
|
output << newLine << "[" << Section << "]" << "\n";
|
|
}
|
|
output << keyValueData;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else
|
|
{ // found, replace it
|
|
|
|
std::string lineData;
|
|
int input_line_number = 0;
|
|
|
|
while (std::getline(input, lineData))
|
|
{
|
|
++input_line_number;
|
|
|
|
// delete old comment if new comment is set
|
|
if (input_line_number == (line_number_mark - 1) && lineData.length() > 0 && lineData[0] == ';' && comment != "")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (input_line_number == line_number_mark)
|
|
{ // replace to this line
|
|
output << keyValueData;
|
|
}
|
|
else
|
|
{
|
|
output << lineData << "\n";
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
INI_DEBUG("error! inicpp lost process of set function");
|
|
return false;
|
|
|
|
} while (false);
|
|
|
|
// clear work
|
|
input.close();
|
|
output.close();
|
|
|
|
std::remove(_configFileName.c_str());
|
|
std::rename(tempFile.c_str(), _configFileName.c_str());
|
|
|
|
// reload
|
|
parse();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool set(const std::string &Section, const std::string &Key, const int Value, const std::string &comment = "")
|
|
{
|
|
std::string stringValue = std::to_string(Value);
|
|
return set(Section, Key, stringValue, comment);
|
|
}
|
|
|
|
bool set(const std::string &Section, const std::string &Key, const double &Value, const std::string &comment = "")
|
|
{
|
|
std::string stringValue = std::to_string(Value);
|
|
return set(Section, Key, stringValue, comment);
|
|
}
|
|
|
|
bool set(const std::string &Section, const std::string &Key, const char &Value, const std::string &comment = "")
|
|
{
|
|
std::string stringValue = ValueProxy::to_string(Value);
|
|
return set(Section, Key, stringValue, comment);
|
|
}
|
|
|
|
// no sections: head of config file
|
|
bool set(const std::string &Key, const std::string &Value)
|
|
{
|
|
return set("", Key, Value, "");
|
|
}
|
|
bool set(const std::string &Key, const char *Value)
|
|
{
|
|
return set("", Key, Value, "");
|
|
}
|
|
template <typename T>
|
|
bool set(const std::string &Key, const T &Value)
|
|
{
|
|
std::string stringValue = std::to_string(Value);
|
|
return set("", Key, stringValue, "");
|
|
}
|
|
|
|
#ifdef _ENBABLE_INICPP_STD_WSTRING_
|
|
bool set(const std::string &Section, const std::string &Key, const std::wstring &Value, const std::string &comment = "")
|
|
{
|
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
|
std::string stringValue = converter.to_bytes(Value);
|
|
|
|
return set(Section, Key, stringValue, comment);
|
|
}
|
|
#endif
|
|
// comment for section name of key
|
|
bool setComment(const std::string &Section, const std::string &Key, const std::string &comment)
|
|
{
|
|
return set(Section, Key, (*this)[Section].toString(Key), comment);
|
|
}
|
|
// comment for no section name of key
|
|
bool setComment(const std::string &Key, const std::string &comment)
|
|
{
|
|
return set("", Key, (*this)[""].toString(Key), comment);
|
|
}
|
|
|
|
bool isSectionExists(const std::string §ionName)
|
|
{
|
|
return _iniData.isSectionExists(sectionName);
|
|
}
|
|
|
|
inline std::list<std::string /*section name*/> sectionsList()
|
|
{
|
|
return _iniData.getSectionsList();
|
|
}
|
|
|
|
inline std::map<std::string /*key*/, std::string /*value*/> sectionMap(const std::string §ionName)
|
|
{
|
|
return _iniData.getSectionMap(sectionName);
|
|
}
|
|
|
|
#ifdef _ENBABLE_INICPP_STD_WSTRING_
|
|
void setFileName(const std::wstring &fileName)
|
|
{
|
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
|
std::string name = converter.to_bytes(fileName);
|
|
|
|
_configFileName = name;
|
|
}
|
|
#else
|
|
void setFileName(const std::string &fileName)
|
|
{
|
|
_configFileName = fileName;
|
|
}
|
|
#endif
|
|
|
|
private:
|
|
bool filterData(std::string &data)
|
|
{
|
|
if (data.length() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (data[0] == ';')
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (data[0] == '#')
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void trimEdges(std::string &data)
|
|
{
|
|
// remove left ' ' and '\t'
|
|
data.erase(data.begin(), std::find_if(data.begin(), data.end(), [](unsigned char c)
|
|
{ return !std::isspace(c); }));
|
|
// remove right ' ' and '\t'
|
|
data.erase(std::find_if(data.rbegin(), data.rend(), [](unsigned char c)
|
|
{ return !std::isspace(c); })
|
|
.base(),
|
|
data.end());
|
|
|
|
// INI_DEBUG("trimEdges data:|" << data << "|");
|
|
}
|
|
|
|
private:
|
|
ini _iniData;
|
|
int _SumOfLines;
|
|
std::fstream _iniFile;
|
|
std::string _configFileName;
|
|
};
|
|
|
|
} // namespace inicpp
|
|
|
|
#endif
|