diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml index 3641e84..15f31e4 100644 --- a/.gitea/workflows/test.yaml +++ b/.gitea/workflows/test.yaml @@ -15,7 +15,8 @@ jobs: shell: ash {0} run: | apk -U upgrade - apk add --no-cache git bash cmake build-base ninja-is-really-ninja boost-dev toml11 + apk add --no-cache git bash cmake build-base ninja-is-really-ninja boost-dev toml11 \ + glib-dev bzip2-dev curl-dev - uses: actions/checkout@v4 - name: Build run: | diff --git a/backend/CMakeLists.txt b/backend/CMakeLists.txt index 96742e8..259a3c6 100644 --- a/backend/CMakeLists.txt +++ b/backend/CMakeLists.txt @@ -2,48 +2,20 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. +find_package(CURL) find_package(PkgConfig REQUIRED) -pkg_check_modules(deps REQUIRED IMPORTED_TARGET glib-2.0 gio-2.0) +pkg_check_modules(deps REQUIRED IMPORTED_TARGET glib-2.0 gio-2.0 bzip2) add_library(backend) target_sources(backend - INTERFACE job.h utils.h pkgtools.h slackpkg.h - PRIVATE job.cc utils.cc pkgtools.cc slackpkg.cc + PUBLIC FILE_SET all_my_modules + TYPE CXX_MODULES FILES job.cpp slackpkg.cpp pkgtools.cpp utils.cpp ) configure_file(config.h.in ${CMAKE_BINARY_DIR}/generated/config.h) include_directories(${CMAKE_BINARY_DIR}/generated/) -target_link_libraries(backend PkgConfig::deps) -# curl_dep = meson.get_compiler('c').find_library('curl') -# bzip2_dep = dependency('bzip2') - -# packagekit_backend_slack_module = shared_module( -# 'pk_backend_slack', -# 'pk-backend-slack.cc', -# 'utils.cc', -# 'pkgtools.cc', -# 'slackpkg.cc', -# 'dl.cc', -# 'job.cc', -# include_directories: packagekit_src_include, -# dependencies: [ -# packagekit_glib2_dep, -# curl_dep, -# gmodule_dep, -# sqlite3_dep, -# bzip2_dep, -# ], -# cpp_args: [ -# '-DG_LOG_DOMAIN="PackageKit-Slackware"', -# '-DLOCALSTATEDIR="@0@"'.format(join_paths(get_option('prefix'), get_option('localstatedir'))), -# '-DLIBDIR="@0@"'.format(join_paths(get_option('prefix'), get_option('libdir'))), -# '-DSYSCONFDIR="@0@"'.format(get_option('sysconfdir')), -# ], -# override_options: ['c_std=c14', 'cpp_std=c++14'], -# install: true, -# install_dir: pk_plugin_dir, -# ) +target_link_libraries(backend PkgConfig::deps CURL::libcurl) # configure_file( # input: 'Slackware.conf.in', diff --git a/backend/job.cc b/backend/job.cpp similarity index 99% rename from backend/job.cc rename to backend/job.cpp index 93842a2..669e02a 100644 --- a/backend/job.cc +++ b/backend/job.cpp @@ -3,6 +3,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +module; + #include "config.h" #include #include @@ -11,12 +13,17 @@ #include #include #include -#include "job.h" -#include "pkgtools.h" -#include "slackpkg.h" +#include +#include -using namespace katja; +export module katja.job; +import katja.utils; +import katja.pkgtools; +import katja.slackpkg; + +namespace katja +{ static GSList *repos = nullptr; void pk_backend_initialize(GKeyFile *conf) @@ -766,3 +773,4 @@ out: g_rmdir(tmp_dir_name); g_free(tmp_dir_name); } +} diff --git a/backend/job.h b/backend/job.h deleted file mode 100644 index a7d9997..0000000 --- a/backend/job.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ -#pragma once - -#include -#include "utils.h" - -void pk_backend_initialize(GKeyFile *conf); -void pk_backend_destroy(); - -katja::JobData *pk_backend_start_job(); -void pk_backend_stop_job(katja::JobData *job_data); - -void pk_backend_search_thread(katja::JobData *job_data, GVariant *params, const char *user_data); -void pk_backend_search_files(katja::JobData *job_data, char **values); - -void pk_backend_get_details(katja::JobData *job_data, char **package_ids); -void pk_backend_resolve(katja::JobData *job_data, char **packages); - -void pk_backend_download_packages(katja::JobData *job_data, char **package_ids, const char *directory); -void pk_backend_install_packages(katja::JobData *job_data, char **package_ids); -void pk_backend_remove_packages(katja::JobData *job_data, char **package_ids); - -void pk_backend_get_updates(katja::JobData *job_data); -void pk_backend_update_packages(katja::JobData *job_data, char **package_ids); -void pk_backend_refresh_cache(katja::JobData *job, bool force); diff --git a/backend/pkgtools.cc b/backend/pkgtools.cc deleted file mode 100644 index e4c8b41..0000000 --- a/backend/pkgtools.cc +++ /dev/null @@ -1,162 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ -#include "config.h" -#include -#include -#include "pkgtools.h" - -namespace katja -{ -/** - * katja::Pkgtools::download: - * @job_data: A #JobData. - * @dest_dir_name: Destination directory. - * @pkg_name: Package name. - * - * Download a package. - * - * Returns: %TRUE on success, %FALSE otherwise. - **/ -bool Pkgtools::download(JobData *job_data, const char *dest_dir_name, char *pkg_name) noexcept -{ - char *dest_filename, *source_url; - bool ret = false; - sqlite3_stmt *statement = nullptr; - CURL *curl = nullptr; - - if ((sqlite3_prepare_v2(job_data->db, - "SELECT location, (full_name || '.' || ext) FROM pkglist " - "WHERE name LIKE @name AND repo_order = @repo_order", - -1, - &statement, - nullptr) != SQLITE_OK)) - { - return false; - } - sqlite3_bind_text(statement, 1, pkg_name, -1, SQLITE_TRANSIENT); - sqlite3_bind_int(statement, 2, this->get_order()); - - if (sqlite3_step(statement) == SQLITE_ROW) - { - dest_filename = g_build_filename(dest_dir_name, sqlite3_column_text(statement, 1), nullptr); - source_url = g_strconcat(this->get_mirror(), - sqlite3_column_text(statement, 0), - "/", - sqlite3_column_text(statement, 1), - nullptr); - - ret = g_file_test(dest_filename, G_FILE_TEST_EXISTS) - || get_file(&curl, source_url, dest_filename) == CURLE_OK; - - if (curl) - { - curl_easy_cleanup(curl); - } - g_free(source_url); - g_free(dest_filename); - } - sqlite3_finalize(statement); - - return ret; -} - -/** - * katja::Pkgtools::install: - * @job_data: A #JobData. - * @pkg_name: Package name. - * - * Install a package. - **/ -void Pkgtools::install(JobData *job_data, char *pkg_name) noexcept -{ - char *pkg_filename, *cmd_line; - sqlite3_stmt *statement = nullptr; - - if ((sqlite3_prepare_v2(job_data->db, - "SELECT (full_name || '.' || ext) FROM pkglist " - "WHERE name LIKE @name AND repo_order = @repo_order", - -1, - &statement, - nullptr) != SQLITE_OK)) - { - return; - } - - sqlite3_bind_text(statement, 1, pkg_name, -1, SQLITE_TRANSIENT); - sqlite3_bind_int(statement, 2, this->get_order()); - - if (sqlite3_step(statement) == SQLITE_ROW) - { - pkg_filename = g_build_filename(LOCALSTATEDIR, - "cache", - "PackageKit", - "downloads", - sqlite3_column_text(statement, 0), - nullptr); - cmd_line = g_strconcat("/sbin/upgradepkg --install-new ", pkg_filename, nullptr); - g_spawn_command_line_sync(cmd_line, nullptr, nullptr, nullptr, nullptr); - g_free(cmd_line); - - g_free(pkg_filename); - } - sqlite3_finalize(statement); -} - -Pkgtools::~Pkgtools() noexcept -{ -} - -/** - * katja::Pkgtools::get_name: - * - * Retrieves the repository name. - * - * Returns: Repository name. - **/ -const char *Pkgtools::get_name() const noexcept -{ - return this->name; -} - -/** - * katja::Pkgtools::get_mirror: - * - * Retrieves the repository mirror. - * - * Returns: Repository mirror. - **/ -const char *Pkgtools::get_mirror() const noexcept -{ - return this->mirror; -} - -/** - * katja::Pkgtools::get_order: - * - * Retrieves the repository order. - * - * Returns: Repository order. - **/ -guint8 Pkgtools::get_order() const noexcept -{ - return this->order; -} - -/** - * katja::Pkgtools:is_blacklisted: - * @pkg: Package name to check for. - * - * Checks whether a package is blacklisted. - * - * Returns: %TRUE if the package is blacklisted, %FALSE otherwise. - **/ -bool Pkgtools::is_blacklisted(const char *pkg) const noexcept -{ - return this->blacklist - && g_regex_match (this->blacklist, pkg, static_cast(0), nullptr); -} - -} diff --git a/backend/pkgtools.cpp b/backend/pkgtools.cpp new file mode 100644 index 0000000..bb04a10 --- /dev/null +++ b/backend/pkgtools.cpp @@ -0,0 +1,191 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +module; + +#include "config.h" +#include +#include +#include +#include + +export module katja.pkgtools; + +import katja.utils; + +export namespace katja +{ +class Pkgtools +{ +public: + /** + * katja::Pkgtools::get_name: + * + * Retrieves the repository name. + * + * Returns: Repository name. + **/ + const char *get_name() const noexcept + { + return this->name; + } + + /** + * katja::Pkgtools::get_mirror: + * + * Retrieves the repository mirror. + * + * Returns: Repository mirror. + **/ + const char *get_mirror() const noexcept + { + return this->mirror; + } + + /** + * katja::Pkgtools::get_order: + * + * Retrieves the repository order. + * + * Returns: Repository order. + **/ + std::uint8_t get_order() const noexcept + { + return this->order; + } + + /** + * katja::Pkgtools:is_blacklisted: + * @pkg: Package name to check for. + * + * Checks whether a package is blacklisted. + * + * Returns: %TRUE if the package is blacklisted, %FALSE otherwise. + **/ + bool is_blacklisted(const char *pkg) const noexcept + { + return this->blacklist + && g_regex_match (this->blacklist, pkg, static_cast(0), nullptr); + } + + virtual ~Pkgtools() noexcept + { + } + + /** + * katja::Pkgtools::download: + * @job_data: A #JobData. + * @dest_dir_name: Destination directory. + * @pkg_name: Package name. + * + * Download a package. + * + * Returns: %TRUE on success, %FALSE otherwise. + **/ + bool download(JobData *job_data, const char *dest_dir_name, char *pkg_name) noexcept + { + char *dest_filename, *source_url; + bool ret = false; + sqlite3_stmt *statement = nullptr; + CURL *curl = nullptr; + + if ((sqlite3_prepare_v2(job_data->db, + "SELECT location, (full_name || '.' || ext) FROM pkglist " + "WHERE name LIKE @name AND repo_order = @repo_order", + -1, + &statement, + nullptr) != SQLITE_OK)) + { + return false; + } + sqlite3_bind_text(statement, 1, pkg_name, -1, SQLITE_TRANSIENT); + sqlite3_bind_int(statement, 2, this->get_order()); + + if (sqlite3_step(statement) == SQLITE_ROW) + { + dest_filename = g_build_filename(dest_dir_name, sqlite3_column_text(statement, 1), nullptr); + source_url = g_strconcat(this->get_mirror(), + sqlite3_column_text(statement, 0), + "/", + sqlite3_column_text(statement, 1), + nullptr); + + ret = g_file_test(dest_filename, G_FILE_TEST_EXISTS) + || get_file(&curl, source_url, dest_filename) == CURLE_OK; + + if (curl) + { + curl_easy_cleanup(curl); + } + g_free(source_url); + g_free(dest_filename); + } + sqlite3_finalize(statement); + + return ret; + } + + /** + * katja::Pkgtools::install: + * @job_data: A #JobData. + * @pkg_name: Package name. + * + * Install a package. + **/ + void install(JobData *job_data, char *pkg_name) noexcept + { + char *pkg_filename, *cmd_line; + sqlite3_stmt *statement = nullptr; + + if ((sqlite3_prepare_v2(job_data->db, + "SELECT (full_name || '.' || ext) FROM pkglist " + "WHERE name LIKE @name AND repo_order = @repo_order", + -1, + &statement, + nullptr) != SQLITE_OK)) + { + return; + } + + sqlite3_bind_text(statement, 1, pkg_name, -1, SQLITE_TRANSIENT); + sqlite3_bind_int(statement, 2, this->get_order()); + + if (sqlite3_step(statement) == SQLITE_ROW) + { + pkg_filename = g_build_filename(LOCALSTATEDIR, + "cache", + "PackageKit", + "downloads", + sqlite3_column_text(statement, 0), + nullptr); + cmd_line = g_strconcat("/sbin/upgradepkg --install-new ", pkg_filename, nullptr); + g_spawn_command_line_sync(cmd_line, nullptr, nullptr, nullptr, nullptr); + g_free(cmd_line); + + g_free(pkg_filename); + } + sqlite3_finalize(statement); + } + + virtual GSList *collect_cache_info (const char *tmpl) noexcept = 0; + virtual void generate_cache(JobData *job_data, const char *tmpl) noexcept = 0; + +protected: + char *name = nullptr; + char *mirror = nullptr; + std::uint8_t order; + GRegex *blacklist = nullptr; +}; + +/** + * katja::cmp_repo: + **/ +int cmp_repo(const void *a, const void *b) +{ + auto repo = static_cast (a); + + return g_strcmp0(repo->get_name(), (char *) b); +} +} diff --git a/backend/pkgtools.h b/backend/pkgtools.h deleted file mode 100644 index 06f1796..0000000 --- a/backend/pkgtools.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ -#pragma once - -#include -#include -#include "utils.h" - -namespace katja -{ -class Pkgtools -{ -public: - const char *get_name() const noexcept; - const char *get_mirror() const noexcept; - std::uint8_t get_order() const noexcept; - bool is_blacklisted(const char *pkg) const noexcept; - - virtual ~Pkgtools() noexcept; - - bool download(JobData *job_data, const char *dest_dir_name, char *pkg_name) noexcept; - void install(JobData *job_data, char *pkg_name) noexcept; - - virtual GSList *collect_cache_info (const char *tmpl) noexcept = 0; - virtual void generate_cache(JobData *job_data, const char *tmpl) noexcept = 0; - -protected: - char *name = nullptr; - char *mirror = nullptr; - std::uint8_t order; - GRegex *blacklist = nullptr; -}; -} diff --git a/backend/slackpkg.cc b/backend/slackpkg.cc deleted file mode 100644 index a750f2e..0000000 --- a/backend/slackpkg.cc +++ /dev/null @@ -1,512 +0,0 @@ -#include -#include -#include -#include -#include -#include "slackpkg.h" -#include "utils.h" - -namespace katja -{ -GHashTable *Slackpkg::cat_map = nullptr; - -/* - * katja::Slackpkg::manifest: - * @job: a #JobData. - * @tmpl: temporary directory. - * @filename: manifest filename - * - * Parse the manifest file and save the file list in the database. - */ -void -Slackpkg::manifest(JobData *job_data, const char *tmpl, char *filename) noexcept -{ - FILE *manifest; - int err, read_len; - unsigned pos; - char buf[max_buf_size], *path, *pkg_filename, *rest = nullptr, *start; - char *full_name = nullptr; - char **line, **lines; - BZFILE *manifest_bz2; - GRegex *pkg_expr = nullptr, *file_expr = nullptr; - GMatchInfo *match_info; - sqlite3_stmt *statement = nullptr; - - path = g_build_filename(tmpl, - this->get_name(), - filename, - nullptr); - manifest = fopen(path, "rb"); - g_free(path); - - if (!manifest) - { - return; - } - if (!(manifest_bz2 = BZ2_bzReadOpen(&err, manifest, 0, 0, nullptr, 0))) - { - goto out; - } - - /* Prepare regular expressions */ - pkg_expr = g_regex_new("^\\|\\|[[:blank:]]+Package:[[:blank:]]+.+\\/(.+)\\.(t[blxg]z$)?", - static_cast (G_REGEX_OPTIMIZE | G_REGEX_DUPNAMES), - static_cast (0), - nullptr); - file_expr = g_regex_new("^[-bcdlps][-r][-w][-xsS][-r][-w][-xsS][-r][-w]" - "[-xtT][[:space:]][^[:space:]]+[[:space:]]+" - "[[:digit:]]+[[:space:]][[:digit:]-]+[[:space:]]" - "[[:digit:]:]+[[:space:]](?!install\\/|\\.)(.*)", - static_cast (G_REGEX_OPTIMIZE | G_REGEX_DUPNAMES), - static_cast (0), - nullptr); - if (!(file_expr) || !(pkg_expr)) - { - goto out; - } - - /* Prepare SQL statements */ - if (sqlite3_prepare_v2(job_data->db, - "INSERT INTO filelist (full_name, filename) VALUES (@full_name, @filename)", - -1, - &statement, - nullptr) != SQLITE_OK) - { - goto out; - } - - sqlite3_exec(job_data->db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr); - while ((read_len = BZ2_bzRead(&err, manifest_bz2, buf, max_buf_size - 1))) - { - if ((err != BZ_OK) && (err != BZ_STREAM_END)) - { - break; - } - buf[read_len] = '\0'; - - /* Split the read text into lines */ - lines = g_strsplit(buf, "\n", 0); - if (rest) - { /* Add to the first line rest characters from the previous read operation */ - start = lines[0]; - lines[0] = g_strconcat(rest, lines[0], nullptr); - g_free(start); - g_free(rest); - } - if (err != BZ_STREAM_END) /* The last line can be incomplete */ - { - pos = g_strv_length(lines) - 1; - rest = lines[pos]; - lines[pos] = nullptr; - } - for (line = lines; *line; line++) - { - if (g_regex_match(pkg_expr, *line, static_cast (0), &match_info)) - { - if (g_match_info_get_match_count(match_info) > 2) - { /* If the extension matches */ - g_free(full_name); - full_name = g_match_info_fetch(match_info, 1); - } - else - { - full_name = nullptr; - } - } - g_match_info_free(match_info); - - match_info = nullptr; - if (full_name && g_regex_match(file_expr, *line, static_cast (0), &match_info)) - { - pkg_filename = g_match_info_fetch(match_info, 1); - sqlite3_bind_text(statement, 1, full_name, -1, SQLITE_TRANSIENT); - sqlite3_bind_text(statement, 2, pkg_filename, -1, SQLITE_TRANSIENT); - sqlite3_step(statement); - sqlite3_clear_bindings(statement); - sqlite3_reset(statement); - g_free(pkg_filename); - } - g_match_info_free(match_info); - } - g_strfreev(lines); - } - - sqlite3_exec(job_data->db, "END TRANSACTION", nullptr, nullptr, nullptr); - g_free(full_name); - BZ2_bzReadClose(&err, manifest_bz2); - -out: - sqlite3_finalize(statement); - if (file_expr) - { - g_regex_unref(file_expr); - } - if (pkg_expr) - { - g_regex_unref(pkg_expr); - } - fclose(manifest); -} - -/** - * katja::Slackpkg::collect_cache_info: - * @tmpl: temporary directory for downloading the files. - * - * Download files needed to get the information like the list of packages - * in available repositories, updates, package descriptions and so on. - * - * Returns: List of files needed for building the cache. - **/ -GSList * -Slackpkg::collect_cache_info (const char *tmpl) noexcept -{ - CURL *curl = nullptr; - char **source_dest; - GSList *file_list = nullptr; - GFile *tmp_dir, *repo_tmp_dir; - - /* Create the temporary directory for the repository */ - tmp_dir = g_file_new_for_path(tmpl); - repo_tmp_dir = g_file_get_child(tmp_dir, this->get_name()); - g_file_make_directory(repo_tmp_dir, nullptr, nullptr); - - /* Download PACKAGES.TXT. These files are most important, break if some of them couldn't be found */ - for (char **cur_priority = this->priority; *cur_priority; cur_priority++) - { - source_dest = static_cast (g_malloc_n(3, sizeof(char *))); - source_dest[0] = g_strconcat(this->get_mirror(), - *cur_priority, - "/PACKAGES.TXT", - nullptr); - source_dest[1] = g_build_filename(tmpl, - this->get_name(), - "PACKAGES.TXT", - nullptr); - source_dest[2] = nullptr; - - if (get_file(&curl, source_dest[0], nullptr) == CURLE_OK) - { - file_list = g_slist_prepend(file_list, source_dest); - } - else - { - g_strfreev(source_dest); - g_slist_free_full(file_list, (GDestroyNotify)g_strfreev); - goto out; - } - - /* Download file lists if available */ - source_dest = static_cast (g_malloc_n(3, sizeof(char *))); - source_dest[0] = g_strconcat(this->get_mirror(), - *cur_priority, - "/MANIFEST.bz2", - nullptr); - source_dest[1] = g_strconcat(tmpl, - "/", this->get_name(), - "/", *cur_priority, "-MANIFEST.bz2", - nullptr); - source_dest[2] = nullptr; - if (get_file(&curl, source_dest[0], nullptr) == CURLE_OK) - { - file_list = g_slist_prepend(file_list, source_dest); - } - else - { - g_strfreev(source_dest); - } - } -out: - g_object_unref(repo_tmp_dir); - g_object_unref(tmp_dir); - - if (curl) - { - curl_easy_cleanup(curl); - } - return file_list; -} - -/** - * katja::Slackpkg::generate_cache: - * @job_data: A #JobData. - * @tmpl: temporary directory for downloading the files. - * - * Download files needed to get the information like the list of packages - * in available repositories, updates, package descriptions and so on. - * - * Returns: List of files needed for building the cache. - **/ -void -Slackpkg::generate_cache(JobData *job_data, const char *tmpl) noexcept -{ - char **pkg_tokens = nullptr; - char *query = nullptr, *filename = nullptr, *location = nullptr, *summary = nullptr, *line, *packages_txt; - unsigned pkg_compressed = 0, pkg_uncompressed = 0; - gushort pkg_name_len; - GString *desc; - GFile *list_file; - GFileInputStream *fin = nullptr; - GDataInputStream *data_in = nullptr; - sqlite3_stmt *insert_statement = nullptr, *update_statement = nullptr, *insert_default_statement = nullptr, *statement; - - /* Check if the temporary directory for this repository exists, then the file metadata have to be generated */ - packages_txt = g_build_filename(tmpl, this->get_name(), "PACKAGES.TXT", nullptr); - list_file = g_file_new_for_path(packages_txt); - fin = g_file_read(list_file, nullptr, nullptr); - g_object_unref(list_file); - g_free(packages_txt); - if (!fin) - { - goto out; - } - /* Remove the old entries from this repository */ - if (sqlite3_prepare_v2(job_data->db, - "DELETE FROM repos WHERE repo LIKE @repo", - -1, - &statement, - nullptr) == SQLITE_OK) - { - sqlite3_bind_text(statement, 1, this->get_name(), -1, SQLITE_TRANSIENT); - sqlite3_step(statement); - sqlite3_finalize(statement); - } - if (sqlite3_prepare_v2(job_data->db, - "INSERT INTO repos (repo_order, repo) VALUES (@repo_order, @repo)", - -1, - &statement, - nullptr) != SQLITE_OK) - { - goto out; - } - sqlite3_bind_int(statement, 1, this->get_order()); - sqlite3_bind_text(statement, 2, this->get_name(), -1, SQLITE_TRANSIENT); - sqlite3_step(statement); - sqlite3_finalize(statement); - - /* Insert new records */ - if ((sqlite3_prepare_v2(job_data->db, - "INSERT OR REPLACE INTO pkglist (full_name, ver, arch, ext, location, " - "summary, desc, compressed, uncompressed, name, repo_order, cat) " - "VALUES (@full_name, @ver, @arch, @ext, @location, @summary, " - "@desc, @compressed, @uncompressed, @name, @repo_order, @cat)", - -1, - &insert_statement, - nullptr) != SQLITE_OK) - || (sqlite3_prepare_v2(job_data->db, - "INSERT OR REPLACE INTO pkglist (full_name, ver, arch, ext, location, " - "summary, desc, compressed, uncompressed, name, repo_order) " - "VALUES (@full_name, @ver, @arch, @ext, @location, @summary, " - "@desc, @compressed, @uncompressed, @name, @repo_order)", - -1, - &insert_default_statement, - nullptr) != SQLITE_OK)) - { - goto out; - } - query = sqlite3_mprintf("UPDATE pkglist SET full_name = @full_name, ver = @ver, arch = @arch, " - "ext = @ext, location = @location, summary = @summary, " - "desc = @desc, compressed = @compressed, uncompressed = @uncompressed " - "WHERE name LIKE @name AND repo_order = %u", - this->get_order()); - if (sqlite3_prepare_v2(job_data->db, query, -1, &update_statement, nullptr) != SQLITE_OK) - { - goto out; - } - - data_in = g_data_input_stream_new(G_INPUT_STREAM(fin)); - desc = g_string_new(""); - - sqlite3_exec(job_data->db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr); - - while ((line = g_data_input_stream_read_line(data_in, nullptr, nullptr, nullptr))) - { - if (!strncmp(line, "PACKAGE NAME: ", 15)) - { - filename = g_strdup(line + 15); - if (this->is_blacklisted (filename)) - { - g_free(filename); - filename = nullptr; - } - } - else if (filename && !strncmp(line, "PACKAGE LOCATION: ", 19)) - { - location = g_strdup(line + 21); /* Exclude ./ at the path beginning */ - } - else if (filename && !strncmp(line, "PACKAGE SIZE (compressed): ", 28)) - { - /* Remove the unit (kilobytes) */ - pkg_compressed = atoi(g_strndup(line + 28, strlen(line + 28) - 2)) * 1024; - } - else if (filename && !strncmp(line, "PACKAGE SIZE (uncompressed): ", 30)) - { - /* Remove the unit (kilobytes) */ - pkg_uncompressed = atoi(g_strndup(line + 30, strlen(line + 30) - 2)) * 1024; - } - else if (filename && !g_strcmp0(line, "PACKAGE DESCRIPTION:")) - { - g_free(line); - line = g_data_input_stream_read_line(data_in, nullptr, nullptr, nullptr); /* Short description */ - - summary = g_strstr_len(line, -1, "("); - if (summary) /* Else summary = nullptr */ - { - summary = g_strndup(summary + 1, strlen(summary) - 2); /* Without ( ) */ - } - pkg_tokens = split_package_name(filename); - pkg_name_len = strlen(pkg_tokens[0]); /* Description begins with pkg_name: */ - } - else if (filename && !strncmp(line, pkg_tokens[0], pkg_name_len)) - { - g_string_append(desc, line + pkg_name_len + 1); - } - else if (filename && !g_strcmp0(line, "")) - { - if (g_strcmp0(location, "patches/packages")) /* Insert a new package */ - { - /* Get the package group based on its location */ - const char *cat = g_strrstr(location, "/"); - if (cat) /* Else cat = nullptr */ - { - cat = static_cast (g_hash_table_lookup(cat_map, cat + 1)); - } - if (cat) - { - statement = insert_statement; - sqlite3_bind_text(insert_statement, 12, cat, -1, SQLITE_TRANSIENT); - } - else - { - statement = insert_default_statement; - } - sqlite3_bind_int(statement, 11, this->get_order()); - } - else /* Update package information if it is a patch */ - { - statement = update_statement; - } - sqlite3_bind_text(statement, 1, pkg_tokens[3], -1, SQLITE_TRANSIENT); - sqlite3_bind_text(statement, 2, pkg_tokens[1], -1, SQLITE_TRANSIENT); - sqlite3_bind_text(statement, 3, pkg_tokens[2], -1, SQLITE_TRANSIENT); - sqlite3_bind_text(statement, 4, pkg_tokens[4], -1, SQLITE_TRANSIENT); - sqlite3_bind_text(statement, 5, location, -1, SQLITE_TRANSIENT); - sqlite3_bind_text(statement, 6, summary, -1, SQLITE_TRANSIENT); - sqlite3_bind_text(statement, 7, desc->str, -1, SQLITE_TRANSIENT); - sqlite3_bind_int(statement, 8, pkg_compressed); - sqlite3_bind_int(statement, 9, pkg_uncompressed); - sqlite3_bind_text(statement, 10, pkg_tokens[0], -1, SQLITE_TRANSIENT); - - sqlite3_step(statement); - sqlite3_clear_bindings(statement); - sqlite3_reset(statement); - - /* Reset for the next package */ - g_strfreev(pkg_tokens); - g_free(filename); - g_free(location); - g_free(summary); - filename = location = summary = nullptr; - g_string_assign(desc, ""); - pkg_compressed = pkg_uncompressed = 0; - } - g_free(line); - } - sqlite3_exec(job_data->db, "END TRANSACTION", nullptr, nullptr, nullptr); - - g_string_free(desc, true); - g_object_unref(data_in); - - /* Parse MANIFEST.bz2 */ - for (char **p = this->priority; *p; p++) - { - filename = g_strconcat(*p, "-MANIFEST.bz2", nullptr); - manifest(job_data, tmpl, filename); - g_free(filename); - } -out: - sqlite3_finalize(update_statement); - sqlite3_free(query); - sqlite3_finalize(insert_default_statement); - sqlite3_finalize(insert_statement); - - if (fin) - { - g_object_unref(fin); - } -} - -Slackpkg::~Slackpkg () noexcept -{ - if (this->blacklist) - { - g_regex_unref (this->blacklist); - } - - g_free (this->name); - g_free (this->mirror); - if (this->priority) - { - g_strfreev (this->priority); - } -} - -/** - * katja::Slackpkg::Slackpkg: - * @name: Repository name. - * @mirror: Repository mirror. - * @order: Repository order. - * @blacklist: Blacklist. - * @priority: Groups priority. - * - * Constructor. - * - * Returns: New #katja::Slackpkg. - **/ -Slackpkg::Slackpkg (const char *name, const char *mirror, - std::uint8_t order, const char *blacklist, char **priority) noexcept -{ - GRegex *regex; - - if (blacklist) - { - regex = static_cast (g_regex_new (blacklist, - G_REGEX_OPTIMIZE, static_cast (0), nullptr)); - } - else - { - regex = nullptr; - } - - this->name = g_strdup (name); - this->mirror = g_strdup (mirror); - - this->order = order; - - this->blacklist = regex; - - this->priority = priority; - - // Initialize category map - if (cat_map == nullptr) - { - cat_map = g_hash_table_new(g_str_hash, g_str_equal); - g_hash_table_insert (cat_map, (void *) "a", (void *) "system"); - g_hash_table_insert (cat_map, (void *) "ap", (void *) "admin-tools"); - g_hash_table_insert (cat_map, (void *) "d", (void *) "programming"); - g_hash_table_insert (cat_map, (void *) "e", (void *) "programming"); - g_hash_table_insert (cat_map, (void *) "f", (void *) "documentation"); - g_hash_table_insert (cat_map, (void *) "k", (void *) "system"); - g_hash_table_insert (cat_map, (void *) "kde", (void *) "desktop-kde"); - g_hash_table_insert (cat_map, (void *) "kdei", (void *) "localization"); - g_hash_table_insert (cat_map, (void *) "l", (void *) "system"); - g_hash_table_insert (cat_map, (void *) "n", (void *) "network"); - g_hash_table_insert (cat_map, (void *) "t", (void *) "publishing"); - g_hash_table_insert (cat_map, (void *) "tcl", (void *) "system"); - g_hash_table_insert (cat_map, (void *) "x", (void *) "desktop-other"); - g_hash_table_insert (cat_map, (void *) "xap", (void *) "accessories"); - g_hash_table_insert (cat_map, (void *) "xfce", (void *) "desktop-xfce"); - g_hash_table_insert (cat_map, (void *) "y", (void *) "games"); - } -} - -} diff --git a/backend/slackpkg.cpp b/backend/slackpkg.cpp new file mode 100644 index 0000000..a053b76 --- /dev/null +++ b/backend/slackpkg.cpp @@ -0,0 +1,518 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +module; + +#include +#include +#include +#include +#include +#include +#include +#include + +export module katja.slackpkg; + +import katja.utils; +import katja.pkgtools; + +export namespace katja +{ +class Slackpkg final : public Pkgtools +{ +public: + /** + * katja::Slackpkg::Slackpkg: + * @name: Repository name. + * @mirror: Repository mirror. + * @order: Repository order. + * @blacklist: Blacklist. + * @priority: Groups priority. + * + * Constructor. + * + * Returns: New #katja::Slackpkg. + **/ + Slackpkg(const char *name, const char *mirror, + std::uint8_t order, const char *blacklist, char **priority) noexcept + { + GRegex *regex; + + if (blacklist) + { + regex = static_cast(g_regex_new(blacklist, + G_REGEX_OPTIMIZE, static_cast(0), nullptr)); + } + else + { + regex = nullptr; + } + + this->name = g_strdup (name); + this->mirror = g_strdup (mirror); + + this->order = order; + + this->blacklist = regex; + + this->priority = priority; + + // Initialize category map + if (cat_map == nullptr) + { + cat_map = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert(cat_map, (void *) "a", (void *) "system"); + g_hash_table_insert(cat_map, (void *) "ap", (void *) "admin-tools"); + g_hash_table_insert(cat_map, (void *) "d", (void *) "programming"); + g_hash_table_insert(cat_map, (void *) "e", (void *) "programming"); + g_hash_table_insert(cat_map, (void *) "f", (void *) "documentation"); + g_hash_table_insert(cat_map, (void *) "k", (void *) "system"); + g_hash_table_insert(cat_map, (void *) "kde", (void *) "desktop-kde"); + g_hash_table_insert(cat_map, (void *) "kdei", (void *) "localization"); + g_hash_table_insert(cat_map, (void *) "l", (void *) "system"); + g_hash_table_insert(cat_map, (void *) "n", (void *) "network"); + g_hash_table_insert(cat_map, (void *) "t", (void *) "publishing"); + g_hash_table_insert(cat_map, (void *) "tcl", (void *) "system"); + g_hash_table_insert(cat_map, (void *) "x", (void *) "desktop-other"); + g_hash_table_insert(cat_map, (void *) "xap", (void *) "accessories"); + g_hash_table_insert(cat_map, (void *) "xfce", (void *) "desktop-xfce"); + g_hash_table_insert(cat_map, (void *) "y", (void *) "games"); + } + } + + ~Slackpkg() noexcept + { + if (this->blacklist) + { + g_regex_unref(this->blacklist); + } + + g_free(this->name); + g_free(this->mirror); + if (this->priority) + { + g_strfreev(this->priority); + } + } + + /** + * katja::Slackpkg::collect_cache_info: + * @tmpl: temporary directory for downloading the files. + * + * Download files needed to get the information like the list of packages + * in available repositories, updates, package descriptions and so on. + * + * Returns: List of files needed for building the cache. + **/ + GSList *collect_cache_info(const char *tmpl) noexcept + { + CURL *curl = nullptr; + char **source_dest; + GSList *file_list = nullptr; + GFile *tmp_dir, *repo_tmp_dir; + + /* Create the temporary directory for the repository */ + tmp_dir = g_file_new_for_path(tmpl); + repo_tmp_dir = g_file_get_child(tmp_dir, this->get_name()); + g_file_make_directory(repo_tmp_dir, nullptr, nullptr); + + /* Download PACKAGES.TXT. These files are most important, break if some of them couldn't be found */ + for (char **cur_priority = this->priority; *cur_priority; cur_priority++) + { + source_dest = static_cast (g_malloc_n(3, sizeof(char *))); + source_dest[0] = g_strconcat(this->get_mirror(), *cur_priority, "/PACKAGES.TXT", nullptr); + source_dest[1] = g_build_filename(tmpl, this->get_name(), "PACKAGES.TXT", nullptr); + source_dest[2] = nullptr; + + if (get_file(&curl, source_dest[0], nullptr) == CURLE_OK) + { + file_list = g_slist_prepend(file_list, source_dest); + } + else + { + g_strfreev(source_dest); + g_slist_free_full(file_list, (GDestroyNotify)g_strfreev); + goto out; + } + + /* Download file lists if available */ + source_dest = static_cast (g_malloc_n(3, sizeof(char *))); + source_dest[0] = g_strconcat(this->get_mirror(), *cur_priority, "/MANIFEST.bz2", nullptr); + source_dest[1] = g_strconcat(tmpl, "/", this->get_name(), "/", *cur_priority, "-MANIFEST.bz2", nullptr); + source_dest[2] = nullptr; + if (get_file(&curl, source_dest[0], nullptr) == CURLE_OK) + { + file_list = g_slist_prepend(file_list, source_dest); + } + else + { + g_strfreev(source_dest); + } + } + out: + g_object_unref(repo_tmp_dir); + g_object_unref(tmp_dir); + + if (curl) + { + curl_easy_cleanup(curl); + } + return file_list; + } + + /** + * katja::Slackpkg::generate_cache: + * @job_data: A #JobData. + * @tmpl: temporary directory for downloading the files. + * + * Download files needed to get the information like the list of packages + * in available repositories, updates, package descriptions and so on. + * + * Returns: List of files needed for building the cache. + **/ + void generate_cache(JobData *job_data, const char *tmpl) noexcept + { + char **pkg_tokens = nullptr; + char *query = nullptr, *filename = nullptr, *location = nullptr, *summary = nullptr, *line, *packages_txt; + unsigned pkg_compressed = 0, pkg_uncompressed = 0; + gushort pkg_name_len; + GString *desc; + GFile *list_file; + GFileInputStream *fin = nullptr; + GDataInputStream *data_in = nullptr; + sqlite3_stmt *insert_statement = nullptr, *update_statement = nullptr, *insert_default_statement = nullptr; + sqlite3_stmt *statement; + + /* Check if the temporary directory for this repository exists, then the file metadata have to be generated */ + packages_txt = g_build_filename(tmpl, this->get_name(), "PACKAGES.TXT", nullptr); + list_file = g_file_new_for_path(packages_txt); + fin = g_file_read(list_file, nullptr, nullptr); + g_object_unref(list_file); + g_free(packages_txt); + if (!fin) + { + goto out; + } + /* Remove the old entries from this repository */ + if (sqlite3_prepare_v2(job_data->db, + "DELETE FROM repos WHERE repo LIKE @repo", + -1, + &statement, + nullptr) == SQLITE_OK) + { + sqlite3_bind_text(statement, 1, this->get_name(), -1, SQLITE_TRANSIENT); + sqlite3_step(statement); + sqlite3_finalize(statement); + } + if (sqlite3_prepare_v2(job_data->db, + "INSERT INTO repos (repo_order, repo) VALUES (@repo_order, @repo)", + -1, + &statement, + nullptr) != SQLITE_OK) + { + goto out; + } + sqlite3_bind_int(statement, 1, this->get_order()); + sqlite3_bind_text(statement, 2, this->get_name(), -1, SQLITE_TRANSIENT); + sqlite3_step(statement); + sqlite3_finalize(statement); + + /* Insert new records */ + if ((sqlite3_prepare_v2(job_data->db, + "INSERT OR REPLACE INTO pkglist (full_name, ver, arch, ext, location, " + "summary, desc, compressed, uncompressed, name, repo_order, cat) " + "VALUES (@full_name, @ver, @arch, @ext, @location, @summary, " + "@desc, @compressed, @uncompressed, @name, @repo_order, @cat)", + -1, + &insert_statement, + nullptr) != SQLITE_OK) + || (sqlite3_prepare_v2(job_data->db, + "INSERT OR REPLACE INTO pkglist (full_name, ver, arch, ext, location, " + "summary, desc, compressed, uncompressed, name, repo_order) " + "VALUES (@full_name, @ver, @arch, @ext, @location, @summary, " + "@desc, @compressed, @uncompressed, @name, @repo_order)", + -1, + &insert_default_statement, + nullptr) != SQLITE_OK)) + { + goto out; + } + query = sqlite3_mprintf("UPDATE pkglist SET full_name = @full_name, ver = @ver, arch = @arch, " + "ext = @ext, location = @location, summary = @summary, " + "desc = @desc, compressed = @compressed, uncompressed = @uncompressed " + "WHERE name LIKE @name AND repo_order = %u", + this->get_order()); + if (sqlite3_prepare_v2(job_data->db, query, -1, &update_statement, nullptr) != SQLITE_OK) + { + goto out; + } + + data_in = g_data_input_stream_new(G_INPUT_STREAM(fin)); + desc = g_string_new(""); + + sqlite3_exec(job_data->db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr); + + while ((line = g_data_input_stream_read_line(data_in, nullptr, nullptr, nullptr))) + { + if (!strncmp(line, "PACKAGE NAME: ", 15)) + { + filename = g_strdup(line + 15); + if (this->is_blacklisted(filename)) + { + g_free(filename); + filename = nullptr; + } + } + else if (filename && !strncmp(line, "PACKAGE LOCATION: ", 19)) + { + location = g_strdup(line + 21); /* Exclude ./ at the path beginning */ + } + else if (filename && !strncmp(line, "PACKAGE SIZE (compressed): ", 28)) + { + /* Remove the unit (kilobytes) */ + pkg_compressed = atoi(g_strndup(line + 28, strlen(line + 28) - 2)) * 1024; + } + else if (filename && !strncmp(line, "PACKAGE SIZE (uncompressed): ", 30)) + { + /* Remove the unit (kilobytes) */ + pkg_uncompressed = atoi(g_strndup(line + 30, strlen(line + 30) - 2)) * 1024; + } + else if (filename && !g_strcmp0(line, "PACKAGE DESCRIPTION:")) + { + g_free(line); + line = g_data_input_stream_read_line(data_in, nullptr, nullptr, nullptr); /* Short description */ + + summary = g_strstr_len(line, -1, "("); + if (summary) /* Else summary = nullptr */ + { + summary = g_strndup(summary + 1, strlen(summary) - 2); /* Without ( ) */ + } + pkg_tokens = split_package_name(filename); + pkg_name_len = strlen(pkg_tokens[0]); /* Description begins with pkg_name: */ + } + else if (filename && !strncmp(line, pkg_tokens[0], pkg_name_len)) + { + g_string_append(desc, line + pkg_name_len + 1); + } + else if (filename && !g_strcmp0(line, "")) + { + if (g_strcmp0(location, "patches/packages")) /* Insert a new package */ + { + /* Get the package group based on its location */ + const char *cat = g_strrstr(location, "/"); + if (cat) /* Else cat = nullptr */ + { + cat = static_cast(g_hash_table_lookup(cat_map, cat + 1)); + } + if (cat) + { + statement = insert_statement; + sqlite3_bind_text(insert_statement, 12, cat, -1, SQLITE_TRANSIENT); + } + else + { + statement = insert_default_statement; + } + sqlite3_bind_int(statement, 11, this->get_order()); + } + else /* Update package information if it is a patch */ + { + statement = update_statement; + } + sqlite3_bind_text(statement, 1, pkg_tokens[3], -1, SQLITE_TRANSIENT); + sqlite3_bind_text(statement, 2, pkg_tokens[1], -1, SQLITE_TRANSIENT); + sqlite3_bind_text(statement, 3, pkg_tokens[2], -1, SQLITE_TRANSIENT); + sqlite3_bind_text(statement, 4, pkg_tokens[4], -1, SQLITE_TRANSIENT); + sqlite3_bind_text(statement, 5, location, -1, SQLITE_TRANSIENT); + sqlite3_bind_text(statement, 6, summary, -1, SQLITE_TRANSIENT); + sqlite3_bind_text(statement, 7, desc->str, -1, SQLITE_TRANSIENT); + sqlite3_bind_int(statement, 8, pkg_compressed); + sqlite3_bind_int(statement, 9, pkg_uncompressed); + sqlite3_bind_text(statement, 10, pkg_tokens[0], -1, SQLITE_TRANSIENT); + + sqlite3_step(statement); + sqlite3_clear_bindings(statement); + sqlite3_reset(statement); + + /* Reset for the next package */ + g_strfreev(pkg_tokens); + g_free(filename); + g_free(location); + g_free(summary); + filename = location = summary = nullptr; + g_string_assign(desc, ""); + pkg_compressed = pkg_uncompressed = 0; + } + g_free(line); + } + sqlite3_exec(job_data->db, "END TRANSACTION", nullptr, nullptr, nullptr); + + g_string_free(desc, true); + g_object_unref(data_in); + + /* Parse MANIFEST.bz2 */ + for (char **p = this->priority; *p; p++) + { + filename = g_strconcat(*p, "-MANIFEST.bz2", nullptr); + manifest(job_data, tmpl, filename); + g_free(filename); + } + out: + sqlite3_finalize(update_statement); + sqlite3_free(query); + sqlite3_finalize(insert_default_statement); + sqlite3_finalize(insert_statement); + + if (fin) + { + g_object_unref(fin); + } + } + +private: + static inline GHashTable *cat_map{ nullptr }; + static const std::size_t max_buf_size = 8192; + char **priority = nullptr; + + /* + * katja::Slackpkg::manifest: + * @job: a #JobData. + * @tmpl: temporary directory. + * @filename: manifest filename + * + * Parse the manifest file and save the file list in the database. + */ + + void manifest(JobData *job_data, const char *tmpl, char *filename) noexcept + { + FILE *manifest; + int err, read_len; + unsigned pos; + char buf[max_buf_size], *path, *pkg_filename, *rest = nullptr, *start; + char *full_name = nullptr; + char **line, **lines; + BZFILE *manifest_bz2; + GRegex *pkg_expr = nullptr, *file_expr = nullptr; + GMatchInfo *match_info; + sqlite3_stmt *statement = nullptr; + + path = g_build_filename(tmpl, + this->get_name(), + filename, + nullptr); + manifest = fopen(path, "rb"); + g_free(path); + + if (!manifest) + { + return; + } + if (!(manifest_bz2 = BZ2_bzReadOpen(&err, manifest, 0, 0, nullptr, 0))) + { + goto out; + } + + /* Prepare regular expressions */ + pkg_expr = g_regex_new("^\\|\\|[[:blank:]]+Package:[[:blank:]]+.+\\/(.+)\\.(t[blxg]z$)?", + static_cast (G_REGEX_OPTIMIZE | G_REGEX_DUPNAMES), + static_cast (0), + nullptr); + file_expr = g_regex_new("^[-bcdlps][-r][-w][-xsS][-r][-w][-xsS][-r][-w]" + "[-xtT][[:space:]][^[:space:]]+[[:space:]]+" + "[[:digit:]]+[[:space:]][[:digit:]-]+[[:space:]]" + "[[:digit:]:]+[[:space:]](?!install\\/|\\.)(.*)", + static_cast (G_REGEX_OPTIMIZE | G_REGEX_DUPNAMES), + static_cast (0), + nullptr); + if (!(file_expr) || !(pkg_expr)) + { + goto out; + } + + /* Prepare SQL statements */ + if (sqlite3_prepare_v2(job_data->db, + "INSERT INTO filelist (full_name, filename) VALUES (@full_name, @filename)", + -1, + &statement, + nullptr) != SQLITE_OK) + { + goto out; + } + + sqlite3_exec(job_data->db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr); + while ((read_len = BZ2_bzRead(&err, manifest_bz2, buf, max_buf_size - 1))) + { + if ((err != BZ_OK) && (err != BZ_STREAM_END)) + { + break; + } + buf[read_len] = '\0'; + + /* Split the read text into lines */ + lines = g_strsplit(buf, "\n", 0); + if (rest) + { /* Add to the first line rest characters from the previous read operation */ + start = lines[0]; + lines[0] = g_strconcat(rest, lines[0], nullptr); + g_free(start); + g_free(rest); + } + if (err != BZ_STREAM_END) /* The last line can be incomplete */ + { + pos = g_strv_length(lines) - 1; + rest = lines[pos]; + lines[pos] = nullptr; + } + for (line = lines; *line; line++) + { + if (g_regex_match(pkg_expr, *line, static_cast (0), &match_info)) + { + if (g_match_info_get_match_count(match_info) > 2) + { /* If the extension matches */ + g_free(full_name); + full_name = g_match_info_fetch(match_info, 1); + } + else + { + full_name = nullptr; + } + } + g_match_info_free(match_info); + + match_info = nullptr; + if (full_name && g_regex_match(file_expr, *line, static_cast (0), &match_info)) + { + pkg_filename = g_match_info_fetch(match_info, 1); + sqlite3_bind_text(statement, 1, full_name, -1, SQLITE_TRANSIENT); + sqlite3_bind_text(statement, 2, pkg_filename, -1, SQLITE_TRANSIENT); + sqlite3_step(statement); + sqlite3_clear_bindings(statement); + sqlite3_reset(statement); + g_free(pkg_filename); + } + g_match_info_free(match_info); + } + g_strfreev(lines); + } + + sqlite3_exec(job_data->db, "END TRANSACTION", nullptr, nullptr, nullptr); + g_free(full_name); + BZ2_bzReadClose(&err, manifest_bz2); + + out: + sqlite3_finalize(statement); + if (file_expr) + { + g_regex_unref(file_expr); + } + if (pkg_expr) + { + g_regex_unref(pkg_expr); + } + fclose(manifest); + } +}; +} diff --git a/backend/slackpkg.h b/backend/slackpkg.h deleted file mode 100644 index 7cda41d..0000000 --- a/backend/slackpkg.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ -#pragma once - -#include -#include -#include "pkgtools.h" -#include "utils.h" - -namespace katja -{ -class Slackpkg final : public Pkgtools -{ -public: - Slackpkg (const char *name, const char *mirror, - std::uint8_t order, const char *blacklist, char **priority) noexcept; - ~Slackpkg () noexcept; - - GSList *collect_cache_info (const char *tmpl) noexcept; - void generate_cache(JobData *job_data, const char *tmpl) noexcept; - -private: - static GHashTable *cat_map; - static const std::size_t max_buf_size = 8192; - char **priority = nullptr; - - void manifest(JobData *job_data, const char *tmpl, char *filename) noexcept; -}; - -} diff --git a/backend/utils.cc b/backend/utils.cpp similarity index 89% rename from backend/utils.cc rename to backend/utils.cpp index b69a780..086fa42 100644 --- a/backend/utils.cc +++ b/backend/utils.cpp @@ -3,15 +3,45 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +module; + #include #include #include #include -#include "utils.h" -#include "pkgtools.h" +#include +#include -namespace katja +export module katja.utils; + +export namespace katja { +enum class Info +{ + // Error. + unknown, + // Installed in the same version. + installed, + // A different version is installed. + updating, + // Available, but not installed. + installing, + // Available. + available +}; + +struct JobData +{ + sqlite3 *db; + CURL *curl; + + virtual void package(Info info, const char *package_id, const char *summary) = 0; + virtual void files(char **) = 0; + virtual void details(char *package_id, + const char *group, const char *description, const char *homepage, int uncompressed) = 0; + virtual void set_percentage(double) = 0; +}; + /** * katja::get_file: * @curl: curl easy handle. @@ -217,14 +247,4 @@ Info is_installed(const char *pkg_fullname) return ret; } -/** - * katja::cmp_repo: - **/ -int cmp_repo(const void *a, const void *b) -{ - auto repo = static_cast (a); - - return g_strcmp0(repo->get_name(), (char *) b); -} - } diff --git a/backend/utils.h b/backend/utils.h deleted file mode 100644 index b3976b8..0000000 --- a/backend/utils.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ -#pragma once - -#include -#include - -namespace katja -{ -enum class Info -{ - // Error. - unknown, - // Installed in the same version. - installed, - // A different version is installed. - updating, - // Available, but not installed. - installing, - // Available. - available -}; - -struct JobData -{ - sqlite3 *db; - CURL *curl; - - virtual void package(Info info, const char *package_id, const char *summary) = 0; - virtual void files(char **) = 0; - virtual void details(char *package_id, - const char *group, const char *description, const char *homepage, int uncompressed) = 0; - virtual void set_percentage(double) = 0; -}; - -CURLcode get_file(CURL **curl, char *source_url, char *dest); - -char **split_package_name(const char *pkg_filename); - -Info is_installed(const char *pkg_fullname); - -int cmp_repo(const void *a, const void *b); - -}