#include "config.h" #include "sbo.h" #include #include #include 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> 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 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 sbo::collect_packages() const { std::unordered_map 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 sbo::list(const std::set& package_names) { std::string line; std::unordered_map packages; boost::process::environment slackbuild_environment; slackbuild_environment["PRINT_PACKAGE_NAME"] = "y"; std::unordered_map package_categories = collect_packages(); for (const std::string& package_name : package_names) { std::unordered_map::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 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 sbo::check_dependencies(const std::string& package_name) { std::unordered_map package_categories = collect_packages(); ordered_set resolved; resolve_dependencies(package_name, package_categories, resolved); std::forward_list 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& package_categories, ordered_set& resolved) const { std::unordered_map::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); } }