/*******************************************************************************
**
** nyamp: yet another media player
** Copyright (C) 2017 Aqua-sama
**
** 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.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
**
******************************************************************************/
#include "configuration.h"
#include
#include
using namespace libconfig;
Configuration::Configuration()
{
m_userCfg = new Config();
#ifdef C_LIKE_CONFIG
m_userCfg->setOptions(Config::OptionSemicolonSeparators | Config::OptionOpenBraceOnSeparateLine);
#endif
// fsync after writing and before closing
m_userCfg->setOption(Config::OptionFsync, true);
m_defaultCfg = new Config();
}
Configuration::~Configuration()
{
if(!m_userCfgPath.empty()) {
m_userCfg->writeFile(m_userCfgPath.c_str());
}
delete m_userCfg;
delete m_defaultCfg;
}
bool Configuration::readUserConfiguration(const std::string &path)
{
m_userCfgPath = path;
try {
m_userCfg->readFile(path.c_str());
} catch (FileIOException) {
return false;
} catch (ParseException) {
return false;
}
return true;
}
bool Configuration::writeUserConfiguration(const std::string &path)
{
m_userCfgPath = path;
try {
m_userCfg->writeFile(path.c_str());
} catch (FileIOException) {
return false;
}
return true;
}
bool Configuration::readDefaultConfiguration(const std::string &data)
{
try {
m_defaultCfg->readString(data);
} catch(ParseException) {
return false;
}
return true;
}
std::vector Configuration::childrenSettings(const char* name)
{
std::vector groupNames;
const Setting &root = m_defaultCfg->lookup(name);
for(const Setting &setting : root) {
if(setting.getType() != Setting::TypeGroup) {
groupNames.push_back(setting.getName());
}
}
return groupNames;
}
std::vector Configuration::childrenGroups(const char* name)
{
std::vector groupNames;
const Setting &root = m_defaultCfg->lookup(name);
for(const Setting &setting : root) {
if(setting.getType() == Setting::TypeGroup) {
groupNames.push_back(setting.getName());
}
}
return groupNames;
}
template
std::optional Configuration::value(const char* path) const
{
Config *conf = getConfig(path);
if(conf == nullptr) {
return std::nullopt;
}
// setting was found
const Setting &setting = conf->lookup(path);
std::optional r;
// cast depending on type
// type checks are done during compile time
switch (setting.getType()) {
case Setting::TypeNone:
r = std::nullopt;
break;
case Setting::TypeInt:
// int, unsigned int, long, unsigned long
if constexpr(std::is_same_v) {
r = std::optional(std::to_string(static_cast(setting)));
} else if constexpr(std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) {
r = std::optional(static_cast(setting));
} else {
r = std::nullopt;
}
break;
case Setting::TypeInt64:
// int, unsigned int; long long, unsigned long long
if constexpr(std::is_same_v) {
r = std::optional(std::to_string(static_cast(setting)));
} else if constexpr(std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) {
r = std::optional(static_cast(setting));
} else {
r = std::nullopt;
}
break;
case Setting::TypeFloat:
// float, double
if constexpr(std::is_same_v) {
r = std::optional(std::to_string(static_cast(setting)));
} else if constexpr(std::is_same_v || std::is_same_v) {
r = std::optional(static_cast(setting));
} else {
r = std::nullopt;
}
break;
case Setting::TypeString:
// const char*, std::string
if constexpr(std::is_same::value) {
r = std::optional(static_cast(setting));
} else {
r = std::nullopt;
}
break;
case Setting::TypeBoolean:
// bool
if constexpr(std::is_same::value) {
r = std::optional(static_cast(setting) ? "true" : "false");
} else if constexpr(std::is_same::value) {
r = std::optional(static_cast(setting));
} else {
r = std::nullopt;
}
break;
case Setting::TypeGroup:
r = std::nullopt;
break;
case Setting::TypeArray:
r = std::nullopt;
break;
case Setting::TypeList:
r = std::nullopt;
break;
}
return r;
}
// tell the compiler to export these functions, otherwise you get linking errors
template std::optional Configuration::value(const char* path) const;
template std::optional Configuration::value(const char* path) const;
template std::optional Configuration::value(const char* path) const;
template std::optional Configuration::value(const char* path) const;
template std::optional Configuration::value(const char* path) const;
template std::optional Configuration::value(const char* path) const;
template std::optional Configuration::value(const char* path) const;
template std::optional Configuration::value(const char* path) const;
template std::optional Configuration::value(const char* path) const;
template std::optional Configuration::value(const char* path) const;
template
void Configuration::setValue(std::string path, const T &val)
{
if(m_userCfg->exists(path)) {
Setting &setting = m_userCfg->lookup(path);
// compiler complained about operator= not taking unsinged ints, longs and long longs
if constexpr(std::is_unsigned_v && !std::is_same_v) {
setting = static_cast>(val);
} else {
setting = val;
}
return;
}
// the entry doesn't exist, so we need to create it
// libconfig can't create an entry from a path, we need to get its root, and then add it
// this will error out if entry being added is not in the default config
std::istringstream p(path);
Setting *userSetting = &m_userCfg->getRoot();
Setting *defaultSetting = &m_defaultCfg->getRoot();
std::string i;
while (std::getline(p, i, '.')) {
defaultSetting = &defaultSetting->lookup(i);
// check if user setting exists, if not, create it
if(!userSetting->exists(i)) {
userSetting = &userSetting->add(i, defaultSetting->getType());
}
}
if constexpr(std::is_unsigned_v && !std::is_same_v) {
*userSetting = static_cast>(val);
} else {
*userSetting = val;
}
}
template void Configuration::setValue(std::string path, const int &val);
template void Configuration::setValue(std::string path, const unsigned int &val);
template void Configuration::setValue(std::string path, const long &val);
template void Configuration::setValue(std::string path, const unsigned long &val);
template void Configuration::setValue(std::string path, const long long &val);
template void Configuration::setValue(std::string path, const unsigned long long &val);
template void Configuration::setValue(std::string path, const float &val);
template void Configuration::setValue(std::string path, const double &val);
template void Configuration::setValue(std::string path, const bool &val);
template void Configuration::setValue(std::string path, const std::string &val);
Config *Configuration::getConfig(const char* path) const
{
if(m_userCfg->exists(path)) {
return m_userCfg;
} else if(m_defaultCfg->exists(path)) {
return m_defaultCfg;
} else {
return nullptr;
}
}
std::string &patchHome(std::string &path, const std::string &home)
{
const size_t location = path.find("~");
if(location != std::string::npos) {
return path.replace(location, 1, home);
}
return path;
}