arch-repo-manager/libpkg/parser/config.cpp

205 lines
8.7 KiB
C++

#include "./config.h"
#include <c++utilities/conversion/stringbuilder.h>
#include <c++utilities/io/ansiescapecodes.h>
#include <c++utilities/io/inifile.h>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <unordered_map>
#include <sys/utsname.h> // for uname
using namespace std;
using namespace CppUtilities;
using namespace CppUtilities::EscapeCodes;
namespace LibPkg {
static void moveLastValue(string &target, multimap<string, string> &multimap, const string &key)
{
const auto it = find_if(multimap.rbegin(), multimap.rend(), [&key](const pair<string, string> &i) { return i.first == key; });
if (it != multimap.rend()) {
target = move(it->second);
}
}
static void moveValues(vector<string> &target, multimap<string, string> &multimap, const string &key)
{
for (auto range = multimap.equal_range(key); range.first != range.second; ++range.first) {
target.emplace_back(move(range.first->second));
}
}
void Config::loadPacmanConfig(const char *pacmanConfigPath)
{
// open and parse ini
IniFile configIni;
unordered_map<string, IniFile> includedInis;
{
ifstream configFile;
configFile.exceptions(ios_base::failbit | ios_base::badbit);
configFile.open(pacmanConfigPath, ios_base::in);
configIni.parse(configFile);
}
auto &configData = configIni.data();
auto architecture = std::string_view{};
// read options and create Database object for each db
for (auto &scope : configData) {
if (scope.first == "options") {
// read global options or assume defaults
auto &options = scope.second;
for (auto range = options.equal_range("Architecture"); range.first != range.second; ++range.first) {
if (range.first->second != "auto") {
architecture = *architectures.emplace(move(range.first->second)).first;
} else {
struct utsname un;
uname(&un);
architecture = *architectures.emplace(un.machine).first;
}
}
moveLastValue(pacmanDatabasePath, options, "DBPath");
if (pacmanDatabasePath.empty()) {
pacmanDatabasePath = "/var/lib/pacman/";
}
moveValues(packageCacheDirs, options, "CacheDir");
if (packageCacheDirs.empty()) {
packageCacheDirs.emplace_back("/var/cache/pacman/pkg/");
}
string sigLevel;
moveLastValue(sigLevel, options, "SigLevel");
signatureLevel = SignatureLevelConfig::fromString(sigLevel);
if (!signatureLevel.isValid()) {
signatureLevel = SignatureLevelConfig();
cerr << Phrases::WarningMessage << "The global/default signature level \"" << sigLevel << "\" is invalid and will be ignored."
<< Phrases::End << Phrases::SubWarning << "Assuming default \"" << signatureLevel.toString() << "\" instead"
<< Phrases::EndFlush;
}
} else {
// read sync database
auto *const db = findOrCreateDatabase(move(scope.first), architecture);
// read sig level
string sigLevel;
moveLastValue(sigLevel, scope.second, "SigLevel");
const auto dbSpecificSignatureLevelConfig = SignatureLevelConfig::fromString(sigLevel);
if (dbSpecificSignatureLevelConfig.databaseScope != SignatureLevel::Invalid) {
db->signatureLevel = dbSpecificSignatureLevelConfig.databaseScope;
} else {
cerr << Phrases::WarningMessage << "The signature level \"" << sigLevel << "\" specified for DB \"" << db->name
<< "\" is invalid and will be ignored." << Phrases::End << Phrases::SubWarning << "Assuming global default \""
<< signatureLevelToString(signatureLevel.databaseScope) << "\" instead" << Phrases::EndFlush;
db->signatureLevel = signatureLevel.databaseScope;
}
// add mirrors
for (auto range = scope.second.equal_range("Server"); range.first != range.second; ++range.first) {
for (const auto &arch : architectures) {
string url = range.first->second;
findAndReplace<string>(url, "$repo", db->name);
findAndReplace<string>(url, "$arch", arch);
db->mirrors.emplace_back(move(url));
}
}
// add included mirrors
for (auto includeRange = scope.second.equal_range("Include"); includeRange.first != includeRange.second; ++includeRange.first) {
const auto &path = includeRange.first->second;
auto &includedIni = includedInis[path];
if (includedIni.data().empty()) {
try {
ifstream includedFile;
includedFile.exceptions(ios_base::failbit | ios_base::badbit);
includedFile.open(path, ios_base::in);
includedIni.parse(includedFile);
} catch (const ios_base::failure &) {
cerr << Phrases::WarningMessage << "An IO error occurred when parsing the included file \"" << path << "\"."
<< Phrases::EndFlush;
}
}
for (auto &nestedScope : includedIni.data()) {
if (!nestedScope.first.empty()) {
continue;
}
for (auto serverRange = nestedScope.second.equal_range("Server"); serverRange.first != serverRange.second; ++serverRange.first) {
for (const auto &arch : architectures) {
string url = serverRange.first->second;
findAndReplace<string>(url, "$repo", db->name);
findAndReplace<string>(url, "$arch", arch);
db->mirrors.emplace_back(move(url));
}
}
}
}
// set database file paths
if (db->localDbDir.empty()) {
db->localDbDir = pacmanDatabasePath + "sync";
}
if (db->localPkgDir.empty()) {
db->localPkgDir = packageCacheDirs.front();
}
// ensure the database is not being discarded
db->toBeDiscarded = false;
}
}
}
void Config::loadAllPackages(bool withFiles)
{
for (Database &db : databases) {
try {
db.loadPackages(withFiles);
} catch (const runtime_error &e) {
cerr << Phrases::ErrorMessage << "Unable to load database \"" << db.name << "\": " << e.what() << Phrases::EndFlush;
}
}
}
std::uint64_t Config::restoreFromCache()
{
fstream cacheFile;
cacheFile.exceptions(ios_base::failbit | ios_base::badbit);
cacheFile.open("cache.bin", ios_base::in | ios_base::binary);
restoreFromBinary(cacheFile);
return static_cast<std::uint64_t>(cacheFile.tellg());
}
std::uint64_t Config::dumpCacheFile()
{
fstream cacheFile;
cacheFile.exceptions(ios_base::failbit | ios_base::badbit);
cacheFile.open("cache.bin", ios_base::out | ios_base::trunc | ios_base::binary);
toBinary(cacheFile);
const auto size = static_cast<std::uint64_t>(cacheFile.tellp());
cacheFile.close();
return size;
}
std::pair<std::string_view, std::string_view> Config::parseDatabaseDenotation(std::string_view databaseDenotation)
{
const auto archStart = databaseDenotation.rfind('@');
if (archStart == std::string_view::npos) {
return std::make_pair(databaseDenotation, "x86_64");
} else {
return std::make_pair(databaseDenotation.substr(0, archStart), databaseDenotation.substr(archStart + 1));
}
}
std::tuple<std::string_view, std::string_view, std::string_view> Config::parsePackageDenotation(std::string_view packageDenotation)
{
const char *const end = packageDenotation.data() + packageDenotation.size();
const char *packageName = packageDenotation.data();
for (; packageName != end && *packageName != '/'; ++packageName)
;
if (packageName == end) {
return std::make_tuple(std::string_view(), std::string_view(), packageDenotation);
} else {
const auto &[dbName, dbArch]
= parseDatabaseDenotation(std::string_view(packageDenotation.data(), static_cast<std::size_t>(packageName - packageDenotation.data())));
return std::make_tuple(dbName, dbArch, std::string_view(packageName + 1, static_cast<std::size_t>(end - packageName - 1)));
}
}
} // namespace LibPkg
#include "reflection/config.h"