Initial commit
This commit is contained in:
310
src/ini_handler.cpp
Normal file
310
src/ini_handler.cpp
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
SWR INI Tool - Switchroot INI Configuration Editor
|
||||
Copyright (C) 2026 Switchroot
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "ini_handler.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
static std::string trim(const std::string& str)
|
||||
{
|
||||
size_t first = str.find_first_not_of(" \t\r\n");
|
||||
if (first == std::string::npos)
|
||||
return "";
|
||||
size_t last = str.find_last_not_of(" \t\r\n");
|
||||
return str.substr(first, last - first + 1);
|
||||
}
|
||||
|
||||
static std::string toLower(const std::string& str)
|
||||
{
|
||||
std::string result = str;
|
||||
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
|
||||
return result;
|
||||
}
|
||||
|
||||
IniHandler::IniHandler() : loaded(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool IniHandler::load(const std::string& path)
|
||||
{
|
||||
this->filePath = path;
|
||||
this->lines.clear();
|
||||
this->loaded = false;
|
||||
|
||||
std::ifstream file(path);
|
||||
if (!file.is_open())
|
||||
return false;
|
||||
|
||||
std::string currentSection;
|
||||
std::string rawLine;
|
||||
|
||||
while (std::getline(file, rawLine))
|
||||
{
|
||||
Line line;
|
||||
line.raw = rawLine;
|
||||
|
||||
std::string trimmed = trim(rawLine);
|
||||
|
||||
if (trimmed.empty())
|
||||
{
|
||||
line.type = Line::BLANK;
|
||||
}
|
||||
else if (trimmed[0] == '#' || trimmed[0] == ';')
|
||||
{
|
||||
line.type = Line::COMMENT;
|
||||
}
|
||||
else if (trimmed[0] == '[' && trimmed.back() == ']')
|
||||
{
|
||||
line.type = Line::SECTION;
|
||||
line.section = trim(trimmed.substr(1, trimmed.size() - 2));
|
||||
currentSection = line.section;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t eqPos = trimmed.find('=');
|
||||
if (eqPos != std::string::npos)
|
||||
{
|
||||
line.type = Line::KEY_VALUE;
|
||||
line.section = currentSection;
|
||||
line.key = trim(trimmed.substr(0, eqPos));
|
||||
line.value = trim(trimmed.substr(eqPos + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
line.type = Line::COMMENT; // treat unparseable lines as comments
|
||||
}
|
||||
}
|
||||
|
||||
this->lines.push_back(line);
|
||||
}
|
||||
|
||||
file.close();
|
||||
this->loaded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IniHandler::save()
|
||||
{
|
||||
return save(this->filePath);
|
||||
}
|
||||
|
||||
bool IniHandler::save(const std::string& path)
|
||||
{
|
||||
std::ofstream file(path);
|
||||
if (!file.is_open())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < this->lines.size(); i++)
|
||||
{
|
||||
const Line& line = this->lines[i];
|
||||
|
||||
if (line.type == Line::KEY_VALUE)
|
||||
{
|
||||
file << line.key << "=" << line.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
file << line.raw;
|
||||
}
|
||||
|
||||
if (i < this->lines.size() - 1)
|
||||
file << "\n";
|
||||
}
|
||||
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string IniHandler::get(const std::string& section, const std::string& key, const std::string& defaultValue) const
|
||||
{
|
||||
int idx = findLine(section, key);
|
||||
if (idx >= 0)
|
||||
return this->lines[idx].value;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
int IniHandler::getInt(const std::string& section, const std::string& key, int defaultValue) const
|
||||
{
|
||||
std::string val = get(section, key, "");
|
||||
if (val.empty())
|
||||
return defaultValue;
|
||||
|
||||
try
|
||||
{
|
||||
return std::stoi(val);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
bool IniHandler::getBool(const std::string& section, const std::string& key, bool defaultValue) const
|
||||
{
|
||||
return getInt(section, key, defaultValue ? 1 : 0) != 0;
|
||||
}
|
||||
|
||||
void IniHandler::set(const std::string& section, const std::string& key, const std::string& value)
|
||||
{
|
||||
int idx = findLine(section, key);
|
||||
if (idx >= 0)
|
||||
{
|
||||
this->lines[idx].value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Key doesn't exist - add it at the end of the section
|
||||
int sectionEnd = findSectionEnd(section);
|
||||
|
||||
if (sectionEnd < 0)
|
||||
{
|
||||
// Section doesn't exist - create it
|
||||
Line sectionLine;
|
||||
sectionLine.type = Line::SECTION;
|
||||
sectionLine.section = section;
|
||||
sectionLine.raw = "[" + section + "]";
|
||||
|
||||
Line blankLine;
|
||||
blankLine.type = Line::BLANK;
|
||||
blankLine.raw = "";
|
||||
|
||||
Line kvLine;
|
||||
kvLine.type = Line::KEY_VALUE;
|
||||
kvLine.section = section;
|
||||
kvLine.key = key;
|
||||
kvLine.value = value;
|
||||
|
||||
this->lines.push_back(blankLine);
|
||||
this->lines.push_back(sectionLine);
|
||||
this->lines.push_back(kvLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
Line kvLine;
|
||||
kvLine.type = Line::KEY_VALUE;
|
||||
kvLine.section = section;
|
||||
kvLine.key = key;
|
||||
kvLine.value = value;
|
||||
|
||||
this->lines.insert(this->lines.begin() + sectionEnd, kvLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IniHandler::setInt(const std::string& section, const std::string& key, int value)
|
||||
{
|
||||
set(section, key, std::to_string(value));
|
||||
}
|
||||
|
||||
void IniHandler::setBool(const std::string& section, const std::string& key, bool value)
|
||||
{
|
||||
setInt(section, key, value ? 1 : 0);
|
||||
}
|
||||
|
||||
bool IniHandler::hasSection(const std::string& section) const
|
||||
{
|
||||
for (const Line& line : this->lines)
|
||||
{
|
||||
if (line.type == Line::SECTION && toLower(line.section) == toLower(section))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IniHandler::hasKey(const std::string& section, const std::string& key) const
|
||||
{
|
||||
return findLine(section, key) >= 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> IniHandler::getSections() const
|
||||
{
|
||||
std::vector<std::string> sections;
|
||||
for (const Line& line : this->lines)
|
||||
{
|
||||
if (line.type == Line::SECTION)
|
||||
sections.push_back(line.section);
|
||||
}
|
||||
return sections;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> IniHandler::getKeys(const std::string& section) const
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> keys;
|
||||
for (const Line& line : this->lines)
|
||||
{
|
||||
if (line.type == Line::KEY_VALUE && toLower(line.section) == toLower(section))
|
||||
keys.push_back({line.key, line.value});
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
std::string IniHandler::findOsSection() const
|
||||
{
|
||||
for (const Line& line : this->lines)
|
||||
{
|
||||
if (line.type == Line::SECTION && toLower(line.section) != "config")
|
||||
return line.section;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
int IniHandler::findLine(const std::string& section, const std::string& key) const
|
||||
{
|
||||
std::string sectionLower = toLower(section);
|
||||
std::string keyLower = toLower(key);
|
||||
|
||||
for (size_t i = 0; i < this->lines.size(); i++)
|
||||
{
|
||||
const Line& line = this->lines[i];
|
||||
if (line.type == Line::KEY_VALUE &&
|
||||
toLower(line.section) == sectionLower &&
|
||||
toLower(line.key) == keyLower)
|
||||
{
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int IniHandler::findSectionEnd(const std::string& section) const
|
||||
{
|
||||
std::string sectionLower = toLower(section);
|
||||
bool inSection = false;
|
||||
int lastLine = -1;
|
||||
|
||||
for (size_t i = 0; i < this->lines.size(); i++)
|
||||
{
|
||||
const Line& line = this->lines[i];
|
||||
if (line.type == Line::SECTION)
|
||||
{
|
||||
if (toLower(line.section) == sectionLower)
|
||||
{
|
||||
inSection = true;
|
||||
lastLine = (int)i + 1;
|
||||
}
|
||||
else if (inSection)
|
||||
{
|
||||
return lastLine;
|
||||
}
|
||||
}
|
||||
else if (inSection && line.type != Line::BLANK)
|
||||
{
|
||||
lastLine = (int)i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (inSection)
|
||||
return lastLine;
|
||||
|
||||
return -1;
|
||||
}
|
||||
Reference in New Issue
Block a user