summaryrefslogtreecommitdiff
path: root/src/sbo.cpp
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2023-04-15 08:43:30 +0200
committerEugen Wissner <belka@caraus.de>2023-04-15 08:43:30 +0200
commit34b10f41aa285e423cccb161342b68ae7275da4b (patch)
treee021c107400ec467b59a019c45d6659110c677cf /src/sbo.cpp
parentdbf14caee2f3ffbcfb21d5ca4d1566e0f57a1aed (diff)
downloadslackbuilder-34b10f41aa285e423cccb161342b68ae7275da4b.tar.gz
Retrieve updatable packages
Diffstat (limited to 'src/sbo.cpp')
-rw-r--r--src/sbo.cpp232
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);
+ }
+}