diff options
| author | Eugen Wissner <belka@caraus.de> | 2023-04-15 08:43:30 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2023-04-15 08:43:30 +0200 |
| commit | 34b10f41aa285e423cccb161342b68ae7275da4b (patch) | |
| tree | e021c107400ec467b59a019c45d6659110c677cf /src/sbo.cpp | |
| parent | dbf14caee2f3ffbcfb21d5ca4d1566e0f57a1aed (diff) | |
| download | slackbuilder-34b10f41aa285e423cccb161342b68ae7275da4b.tar.gz | |
Retrieve updatable packages
Diffstat (limited to 'src/sbo.cpp')
| -rw-r--r-- | src/sbo.cpp | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/src/sbo.cpp b/src/sbo.cpp new file mode 100644 index 0000000..086ba79 --- /dev/null +++ b/src/sbo.cpp @@ -0,0 +1,232 @@ +#include "config.h" +#include "sbo.h" +#include <algorithm> +#include <fstream> +#include <vector> + +namespace katja +{ + sbo::sbo() + : git_binary(boost::process::search_path("git")) + , cache_path(std::filesystem::path{ WORKDIR } / "sbo/repository") + { + } + + info sbo::parse_info_file( + const std::filesystem::path& package_directory) const + { + std::map<std::string, std::vector<std::string>> results; + std::filesystem::path info_path = cache_path + / package_directory + / package_directory.filename().replace_extension(".info"); + + if (!std::filesystem::exists(info_path)) + { + throw package_exception(package_directory.filename().string(), "Package doesn't exist."); + } + // Parse the info file. + std::ifstream info_file{ info_path, std::ios::in }; + if (!info_file.is_open()) + { + throw package_exception(package_directory.filename().string(), "Unable to open the .info file."); + } + std::string line; + std::string variable_name; + std::vector<std::string> variable_values; + + while (std::getline(info_file, line)) + { + std::string::const_iterator current_char = line.cbegin(); + + if (variable_name.empty()) + { + current_char = std::find(current_char, line.cend(), '='); + if (current_char == line.cbegin() || current_char == line.cend()) + { + break; // Expected 'variable name' or '='. + } + ++current_char; + if (current_char == line.cend() || *current_char != '"') + { + break; // Expected '"'. + } + variable_name = std::string(line.cbegin(), current_char - 1); + + variable_values.push_back(""); + } + + for (current_char += 1; current_char != line.end(); ++current_char) + { + if (*current_char == ' ') + { + if (!variable_values.back().empty()) + { + variable_values.push_back(""); + } + } + else if (*current_char == '"') + { + if (variable_values.back().empty()) + { + variable_values.pop_back(); + } + results.insert_or_assign(variable_name, variable_values); + variable_name = ""; + variable_values.clear(); + break; + } + else if (*current_char == '\\') + { + break; + } + else + { + variable_values.back().push_back(*current_char); + } + } + if (current_char == line.cend()) + { + break; // Expected '\' or '"'. + } + } + info _info; + + _info.prgnam = results["PRGNAM"][0]; + _info.version = results["VERSION"][0]; + _info.homepage = results["HOMEPAGE"].size() == 0 ? "" : results["HOMEPAGE"][0]; + _info.download = results["DOWNLOAD"]; + _info.md5sum = results["DOWNLOAD"]; + _info.download_x86_64 = results["DOWNLOAD_X86_64"]; + _info.md5sum_x86_64 = results["MD5SUM_X86_64"]; + _info.requires = results["REQUIRES"]; + _info.maintainer = results["MAINTAINER"][0]; + _info.email = results["EMAIL"][0]; + + return _info; + } + + std::unordered_map<std::string, std::filesystem::path> sbo::collect_packages() const + { + std::unordered_map<std::string, std::filesystem::path> results; + + for (const auto& category_directory : std::filesystem::directory_iterator(cache_path)) + { + if (!category_directory.is_directory()) + { + continue; + } + for (const auto& package_directory : std::filesystem::directory_iterator(category_directory.path())) + { + results.insert({ package_directory.path().filename().string(), + category_directory.path().filename() }); + } + } + return results; + } + + std::unordered_map<std::string, package> sbo::list(const std::set<std::string>& package_names) + { + std::string line; + std::unordered_map<std::string, package> packages; + boost::process::environment slackbuild_environment; + slackbuild_environment["PRINT_PACKAGE_NAME"] = "y"; + + std::unordered_map<std::string, std::filesystem::path> package_categories = collect_packages(); + + for (const std::string& package_name : package_names) + { + std::unordered_map<std::string, std::filesystem::path>::iterator package_category = + package_categories.find(package_name); + + if (package_category == package_categories.cend()) + { + continue; + } + std::filesystem::path package_directory_name{ package_category->first }; + std::filesystem::path slackbuild_cwd = cache_path + / package_category->second / package_directory_name; + + // Execute the SlackBuild to get package information. + std::filesystem::path slackbuild_file_name = + package_directory_name.replace_extension(".SlackBuild"); + boost::process::ipstream slackbuild_output; + + boost::process::child slackbuild_process("/bin/bash", slackbuild_file_name.native(), + slackbuild_environment, + boost::process::start_dir(slackbuild_cwd.native()), + boost::process::std_out > slackbuild_output, + boost::process::std_in.close(), + boost::process::std_err > boost::process::null); + + std::optional<package> maybe_package; + if (slackbuild_process.running() && std::getline(slackbuild_output, line) && !line.empty()) + { + maybe_package = package::parse(std::filesystem::path(line).replace_extension("")); + } + slackbuild_process.terminate(); + + if (maybe_package.has_value()) + { + packages.insert({ maybe_package.value().name(), maybe_package.value() }); + } + } + return packages; + } + + void sbo::refresh() + { + std::filesystem::file_status repository_status = std::filesystem::status(cache_path); + + if (std::filesystem::exists(repository_status) + && !std::filesystem::is_directory(repository_status)) + { + throw std::runtime_error("The working directory path \"" + + cache_path.string() + "\" exists, but it isn't a directory."); + } + else if (!std::filesystem::exists(repository_status)) + { + git("clone", std::filesystem::path(), + "git://git.slackbuilds.org/slackbuilds.git", cache_path.native()); + } + git("remote", cache_path.native(), "update", "--prune"); + git("reset", cache_path.native(), "--hard", "origin/master"); + } + + std::forward_list<std::string> sbo::check_dependencies(const std::string& package_name) + { + std::unordered_map<std::string, std::filesystem::path> package_categories = collect_packages(); + ordered_set resolved; + + resolve_dependencies(package_name, package_categories, resolved); + std::forward_list<std::string> results; + + for (const auto& dependency : resolved.get<1>()) + { + results.push_front(dependency); + } + return results; + } + + void sbo::resolve_dependencies(const std::string& package_name, + const std::unordered_map<std::string, std::filesystem::path>& package_categories, + ordered_set& resolved) const + { + std::unordered_map<std::string, std::filesystem::path>::const_iterator package_category = + package_categories.find(package_name); + + if (package_category == package_categories.cend()) + { + throw package_exception(package_name, "Package not found."); + } + info info_file = parse_info_file(package_category->second / package_category->first); + + for (const auto& package_dependency : info_file.requires) + { + if (resolved.find(package_dependency) == resolved.cend()) + { + resolve_dependencies(package_dependency, package_categories, resolved); + } + } + resolved.insert(package_name); + } +} |
