diff options
| author | Jules Blok <jules.blok@gmail.com> | 2015-06-10 00:00:12 +0200 |
|---|---|---|
| committer | Jules Blok <jules.blok@gmail.com> | 2015-06-10 00:00:12 +0200 |
| commit | 7dfced21a2e5aac7195e0e1ad76468e30306766b (patch) | |
| tree | bd671b639d3d911460b767caabad8fc8a759a6bf /Source/Core/Common/IniFile.cpp | |
| parent | c25be031fc1031d795157d8344b87b7841145197 (diff) | |
| parent | 7b0a65e295c784f9d573f2ef52d4e2a7b9bb2889 (diff) | |
Merge branch 'master' into stable
Diffstat (limited to 'Source/Core/Common/IniFile.cpp')
| -rw-r--r-- | Source/Core/Common/IniFile.cpp | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/Source/Core/Common/IniFile.cpp b/Source/Core/Common/IniFile.cpp new file mode 100644 index 0000000000..9ea8f50533 --- /dev/null +++ b/Source/Core/Common/IniFile.cpp @@ -0,0 +1,435 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +// see IniFile.h + +#include <algorithm> +#include <cstddef> +#include <cstring> +#include <fstream> +#include <map> +#include <string> +#include <utility> +#include <vector> + +#include "Common/CommonTypes.h" +#include "Common/FileUtil.h" +#include "Common/IniFile.h" +#include "Common/StringUtil.h" + +void IniFile::ParseLine(const std::string& line, std::string* keyOut, std::string* valueOut) +{ + if (line[0] == '#') + return; + + size_t firstEquals = line.find("=", 0); + + if (firstEquals != std::string::npos) + { + // Yes, a valid line! + *keyOut = StripSpaces(line.substr(0, firstEquals)); + + if (valueOut) + { + *valueOut = StripQuotes(StripSpaces(line.substr(firstEquals + 1, std::string::npos))); + } + } +} + +const std::string& IniFile::NULL_STRING = ""; + +void IniFile::Section::Set(const std::string& key, const std::string& newValue) +{ + auto it = values.find(key); + if (it != values.end()) + it->second = newValue; + else + { + values[key] = newValue; + keys_order.push_back(key); + } +} + +void IniFile::Section::Set(const std::string& key, const std::string& newValue, const std::string& defaultValue) +{ + if (newValue != defaultValue) + Set(key, newValue); + else + Delete(key); +} + +void IniFile::Section::Set(const std::string& key, const std::vector<std::string>& newValues) +{ + std::string temp; + // Join the strings with , + for (const std::string& value : newValues) + { + temp = value + ","; + } + // remove last , + temp.resize(temp.length() - 1); + Set(key, temp); +} + +bool IniFile::Section::Get(const std::string& key, std::string* value, const std::string& defaultValue) +{ + auto it = values.find(key); + if (it != values.end()) + { + *value = it->second; + return true; + } + else if (&defaultValue != &NULL_STRING) + { + *value = defaultValue; + return true; + } + else + return false; +} + +bool IniFile::Section::Get(const std::string& key, std::vector<std::string>* out) +{ + std::string temp; + bool retval = Get(key, &temp); + if (!retval || temp.empty()) + { + return false; + } + // ignore starting , if any + size_t subStart = temp.find_first_not_of(","); + + // split by , + while (subStart != std::string::npos) + { + // Find next , + size_t subEnd = temp.find(',', subStart); + if (subStart != subEnd) + // take from first char until next , + out->push_back(StripSpaces(temp.substr(subStart, subEnd - subStart))); + // Find the next non , char + subStart = temp.find_first_not_of(",", subEnd); + } + return true; +} + +bool IniFile::Section::Get(const std::string& key, int* value, int defaultValue) +{ + std::string temp; + bool retval = Get(key, &temp); + if (retval && TryParse(temp, value)) + return true; + *value = defaultValue; + return false; +} + +bool IniFile::Section::Get(const std::string& key, u32* value, u32 defaultValue) +{ + std::string temp; + bool retval = Get(key, &temp); + if (retval && TryParse(temp, value)) + return true; + *value = defaultValue; + return false; +} + +bool IniFile::Section::Get(const std::string& key, bool* value, bool defaultValue) +{ + std::string temp; + bool retval = Get(key, &temp); + if (retval && TryParse(temp, value)) + return true; + *value = defaultValue; + return false; +} + +bool IniFile::Section::Get(const std::string& key, float* value, float defaultValue) +{ + std::string temp; + bool retval = Get(key, &temp); + if (retval && TryParse(temp, value)) + return true; + *value = defaultValue; + return false; +} + +bool IniFile::Section::Get(const std::string& key, double* value, double defaultValue) +{ + std::string temp; + bool retval = Get(key, &temp); + if (retval && TryParse(temp, value)) + return true; + *value = defaultValue; + return false; +} + +bool IniFile::Section::Exists(const std::string& key) const +{ + return values.find(key) != values.end(); +} + +bool IniFile::Section::Delete(const std::string& key) +{ + auto it = values.find(key); + if (it == values.end()) + return false; + + values.erase(it); + keys_order.erase(std::find(keys_order.begin(), keys_order.end(), key)); + return true; +} + +// IniFile + +const IniFile::Section* IniFile::GetSection(const std::string& sectionName) const +{ + for (const Section& sect : sections) + if (!strcasecmp(sect.name.c_str(), sectionName.c_str())) + return (&(sect)); + return nullptr; +} + +IniFile::Section* IniFile::GetSection(const std::string& sectionName) +{ + for (Section& sect : sections) + if (!strcasecmp(sect.name.c_str(), sectionName.c_str())) + return (&(sect)); + return nullptr; +} + +IniFile::Section* IniFile::GetOrCreateSection(const std::string& sectionName) +{ + Section* section = GetSection(sectionName); + if (!section) + { + sections.emplace_back(sectionName); + section = §ions.back(); + } + return section; +} + +bool IniFile::DeleteSection(const std::string& sectionName) +{ + Section* s = GetSection(sectionName); + if (!s) + return false; + for (auto iter = sections.begin(); iter != sections.end(); ++iter) + { + if (&(*iter) == s) + { + sections.erase(iter); + return true; + } + } + return false; +} + +bool IniFile::Exists(const std::string& sectionName, const std::string& key) const +{ + const Section* section = GetSection(sectionName); + if (!section) + return false; + return section->Exists(key); +} + +void IniFile::SetLines(const std::string& sectionName, const std::vector<std::string> &lines) +{ + Section* section = GetOrCreateSection(sectionName); + section->lines = lines; +} + +bool IniFile::DeleteKey(const std::string& sectionName, const std::string& key) +{ + Section* section = GetSection(sectionName); + if (!section) + return false; + return section->Delete(key); +} + +// Return a list of all keys in a section +bool IniFile::GetKeys(const std::string& sectionName, std::vector<std::string>* keys) const +{ + const Section* section = GetSection(sectionName); + if (!section) + { + return false; + } + *keys = section->keys_order; + return true; +} + +// Return a list of all lines in a section +bool IniFile::GetLines(const std::string& sectionName, std::vector<std::string>* lines, const bool remove_comments) const +{ + lines->clear(); + + const Section* section = GetSection(sectionName); + if (!section) + return false; + + for (std::string line : section->lines) + { + line = StripSpaces(line); + + if (remove_comments) + { + size_t commentPos = line.find('#'); + if (commentPos == 0) + { + continue; + } + + if (commentPos != std::string::npos) + { + line = StripSpaces(line.substr(0, commentPos)); + } + } + + lines->push_back(line); + } + + return true; +} + +void IniFile::SortSections() +{ + sections.sort(); +} + +bool IniFile::Load(const std::string& filename, bool keep_current_data) +{ + if (!keep_current_data) + sections.clear(); + // first section consists of the comments before the first real section + + // Open file + std::ifstream in; + OpenFStream(in, filename, std::ios::in); + + if (in.fail()) + return false; + + Section* current_section = nullptr; + bool first_line = true; + while (!in.eof()) + { + std::string line; + + if (!std::getline(in, line)) + { + if (in.eof()) + return true; + else + return false; + } + + // Skips the UTF-8 BOM at the start of files. Notepad likes to add this. + if (first_line && line.substr(0, 3) == "\xEF\xBB\xBF") + line = line.substr(3); + first_line = false; + +#ifndef _WIN32 + // Check for CRLF eol and convert it to LF + if (!line.empty() && line.at(line.size()-1) == '\r') + { + line.erase(line.size()-1); + } +#endif + + if (line.size() > 0) + { + if (line[0] == '[') + { + size_t endpos = line.find("]"); + + if (endpos != std::string::npos) + { + // New section! + std::string sub = line.substr(1, endpos - 1); + current_section = GetOrCreateSection(sub); + } + } + else + { + if (current_section) + { + std::string key, value; + ParseLine(line, &key, &value); + + // Lines starting with '$', '*' or '+' are kept verbatim. + // Kind of a hack, but the support for raw lines inside an + // INI is a hack anyway. + if ((key == "" && value == "") || + (line.size() >= 1 && + (line[0] == '$' || + line[0] == '+' || + line[0] == '*'))) + current_section->lines.push_back(line); + else + current_section->Set(key, value); + } + } + } + } + + in.close(); + return true; +} + +bool IniFile::Save(const std::string& filename) +{ + std::ofstream out; + std::string temp = File::GetTempFilenameForAtomicWrite(filename); + OpenFStream(out, temp, std::ios::out); + + if (out.fail()) + { + return false; + } + + for (const Section& section : sections) + { + if (section.keys_order.size() != 0 || section.lines.size() != 0) + out << "[" << section.name << "]" << std::endl; + + if (section.keys_order.size() == 0) + { + for (const std::string& s : section.lines) + out << s << std::endl; + } + else + { + for (const std::string& kvit : section.keys_order) + { + auto pair = section.values.find(kvit); + out << pair->first << " = " << pair->second << std::endl; + } + } + } + + out.close(); + + return File::RenameSync(temp, filename); +} + +// Unit test. TODO: Move to the real unit test framework. +/* + int main() + { + IniFile ini; + ini.Load("my.ini"); + ini.Set("Hej", "A", "amaskdfl"); + ini.Set("Mossa", "A", "amaskdfl"); + ini.Set("Aissa", "A", "amaskdfl"); + //ini.Read("my.ini"); + std::string x; + ini.Get("Hej", "B", &x, "boo"); + ini.DeleteKey("Mossa", "A"); + ini.DeleteSection("Mossa"); + ini.SortSections(); + ini.Save("my.ini"); + //UpdateVars(ini); + return 0; + } + */ |
