From 14881b9ffb2e23fb169ae1e9f0955f30467e7d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi> Date: Fri, 10 Jan 2025 23:08:43 +0200 Subject: [PATCH] query: a new api and applet to search and output information super set of info/search/list fixes #5056, #7102, #10653, #10690, #10704 --- doc/Makefile | 1 + doc/apk-info.8.scd | 4 +- doc/apk-query.8.scd | 129 ++++++++++++++ doc/apk.8.scd | 24 ++- doc/meson.build | 1 + src/Makefile | 7 +- src/apk.c | 49 ++---- src/apk_applet.h | 6 +- src/apk_context.h | 3 + src/apk_database.h | 19 +- src/apk_package.h | 1 + src/apk_query.h | 120 +++++++++++++ src/apk_serialize.h | 3 +- src/app_adbdump.c | 12 +- src/app_dot.c | 45 ++--- src/app_fetch.c | 174 +++++-------------- src/app_index.c | 59 +++---- src/app_info.c | 406 +++++++++++++++++-------------------------- src/app_list.c | 205 ++++++++++------------ src/app_policy.c | 88 +++++----- src/app_query.c | 26 +++ src/app_search.c | 104 +++-------- src/applet.c | 12 +- src/database.c | 105 ++--------- src/meson.build | 4 + src/serialize.c | 8 + src/serialize_yaml.c | 2 +- src/solver.c | 4 +- 28 files changed, 762 insertions(+), 859 deletions(-) create mode 100644 doc/apk-query.8.scd create mode 100644 src/apk_query.h create mode 100644 src/app_query.c diff --git a/doc/Makefile b/doc/Makefile index 400de206..49d8ff8f 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -27,6 +27,7 @@ scdocs-y += \ apk-mkndx.8 \ apk-mkpkg.8 \ apk-policy.8 \ + apk-query.8 \ apk-stats.8 \ apk-update.8 \ apk-upgrade.8 \ diff --git a/doc/apk-info.8.scd b/doc/apk-info.8.scd index bbd703bd..1bee547e 100644 --- a/doc/apk-info.8.scd +++ b/doc/apk-info.8.scd @@ -30,10 +30,12 @@ display the appropriate information, then an empty line terminates that field. *-d, --description* Print the package description. -*-e, --installed* +*-e, --exists, --installed* Check package installed status. For each installed package, print it's name. The exit status is the number of given packages not installed. Thus, zero (or success) is returned if all named packages are installed. + NOTE: *--installed* is deprecated and will be removed to allow + the same option in *query* group to function. *-L, --contents* List files included in the package. diff --git a/doc/apk-query.8.scd b/doc/apk-query.8.scd new file mode 100644 index 00000000..1e891a41 --- /dev/null +++ b/doc/apk-query.8.scd @@ -0,0 +1,129 @@ +apk-query(8) + +# NAME + +apk query - query information about packages by various criteria + +# SYNOPSIS + +*apk query* [<_options_>...] _query_... + +*apk query* [<_options_>...] *--recursive* _constraints_... + +# DESCRIPTION + +*apk query* searches for matching packages from selected sources. + +In the default mode, _query_ specifiers are interpreted as follows: + *name{[<>~=]version}* + Select packages by *name* and optional *version* match. + *text* + Select packages by selected fields matching *text*. + +In the *--recursive* mode, the _constraints_ specify a lsit of dependencies +to satisfy and the solver algorithm is used to determine a list of packages +that fullfil these constraints. + +The query executes in the following steps: +. Each _query_ string is executed independently to select candidate packages +. If *--all-matches* is not specified, the best candidate for given term + is added to the list of result packages +. The resulting package list is sorted + +# QUERY OPTIONS + +The applets supporting query specifiers recognize the following options: + +*--all-matches* + Select all matched packages. By default only best match for each query + element is selected. + +*--available* + Filter selection to available packages. + +*--fields* _FIELDS_ + A comma separated list of fields to include in the output. + +*--format* _FORMATSPEC_ + Specify output format from *default*, *yaml* or *json*. The *default* + format is human readable text output. + +*--from* _FROMSPEC_ + Search packages from: *system* (all system sources), *repositories* + (exclude installed database), *installed* (exclude normal repositories) + or *none* (commandline repositories only). + +*--installed* + Filter selection to installed packages. + +*--match* _FIELDS_ + A comma separated list of fields to match the query against. + +*--recursive* + Run solver algorithm with given _constraints_ to select packages. + +*--ugpradable* + Filter selection to upgradable packages. + +*--world* + Include *apk-world*(5) dependencies in constraints. Implies *--recursive*. + +*--oprhaned* + Filter selection to orphaned packages. + +# FIELDS + +The field names are all small letters for *--match* and *--fields* options +and for the machine parseable output (json and yaml). For the human readable +default format the fields are capilaized. + +The following package metadata fields are available: +*name*, *version*, *description*, *arch*, *license*, *origin*, *maintainer*, +*url*, *commit*, *build-time*, *installed-size*, *file-size*, *provider-priority*, +*depends*, *provides*, *replaces*, *install-if*, *layer*, *triggers*, *scripts*, +and *replaces-priority*. +See *apk-package*(8) *package info metadata* and *package metadata* sections +for the description of these fields. + +Additionally the following fields are available: + +*contents* + File names contained in a package. + +*download-url* + Full URL to download the package from. + +*owner* + Lookup owner package for given path name. (*--match* only) + +*package* + The package identifier in format *name*-*version* (e.g. + package-1.0-r0). + +*repositories* + List of repositories the package is available from. + +*status* + Status of an installed package. List of one or more of following + keywords: + - *installed* + - *broken-files* + - *broken-scripts* + - *broken-xattr* + +# EXAMPLES + +\# search all packages starting with apk++ +apk query "apk\*" + +\# show owner package of sensors executable in json++ +apk query --format json --match owner /usr/bin/sensors + +\# show apk-tools and its dependencies in yaml++ +apk query --format yaml --recursive apk-tools + +\# print source packages for all packages providing cmd:apk++ +apk query --match name,provides cmd:apk --fields origin + +\# print source packages with specific dependency name++ +apk query --match dependency so:libapk.so.2.14.9 --fields origin diff --git a/doc/apk.8.scd b/doc/apk.8.scd index 4fc101be..0a1a9c74 100644 --- a/doc/apk.8.scd +++ b/doc/apk.8.scd @@ -46,8 +46,8 @@ Each command is documented in detail on its manual page. ## QUERYING PACKAGE INFORMATION -|[ *apk-info*(8) -:< Give detailed information about packages or repositories +|[ *apk-query*(8) +:< Query information about packages by various criteria | *apk-list*(8) : List packages matching a pattern or other criteria | *apk-dot*(8) @@ -56,6 +56,8 @@ Each command is documented in detail on its manual page. : Show repository policy for packages | *apk-search*(8) : Search for packages by name or description +| *apk-info*(8) +:< Give detailed information about packages or repositories ## REPOSITORY AND PACKAGE MAINTENANCE @@ -200,6 +202,10 @@ The following options are available for all commands. only this directory is processed. The _KEYSDIR_ is treated relative to _ROOT_. +*--legacy-info* + Print output from "info" applet in legacy format. Defaults to yes if + output is not a tty. + *--no-cache* Do not use any local cache path. @@ -209,6 +215,9 @@ The following options are available for all commands. *--no-interactive* Disable interactive mode. +*--no-legacy-info* + Print output from "info" applet in same format as "query" applet. + *--no-logfile* Disable writing to the log file. @@ -284,17 +293,6 @@ The following options are available for all commands which commit the database. force options to minimize failure, and disables commit hooks, among other features. - -# SOURCE OPTIONS - -The following options are available for all commands which operate on the -package indexes only. - -*--from* _FROMSPEC_ - Search packages from: *system* (all system sources), *repositories* - (exclude installed database), *installed* (exclude normal repositories) - or *none* (commandline repositories only). - # GENERATION OPTIONS The following options are available for all commands which generate APKv3 files. diff --git a/doc/meson.build b/doc/meson.build index 2b52e4e7..b85091ee 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -23,6 +23,7 @@ man_filenames = [ 'apk-mkpkg.8.scd', 'apk-package.5.scd', 'apk-policy.8.scd', + 'apk-query.8.scd', 'apk-repositories.5.scd', 'apk-search.8.scd', 'apk-stats.8.scd', diff --git a/src/Makefile b/src/Makefile index 8fc89c1b..db8d5f85 100644 --- a/src/Makefile +++ b/src/Makefile @@ -25,7 +25,7 @@ libapk.so.$(libapk_soname)-objs := \ atom.o balloc.o blob.o commit.o common.o context.o crypto.o crypto_$(CRYPTO).o ctype.o \ database.o hash.o extract_v2.o extract_v3.o fs_fsys.o fs_uvol.o \ io.o io_gunzip.o io_url_$(URL_BACKEND).o tar.o package.o pathbuilder.o print.o process.o \ - repoparser.o serialize.o serialize_json.o serialize_yaml.o solver.o trust.o version.o + query.o repoparser.o serialize.o serialize_json.o serialize_yaml.o solver.o trust.o version.o ifneq ($(URL_BACKEND),wget) CFLAGS_ALL += -Ilibfetch @@ -64,8 +64,9 @@ apk-objs := \ apk.o app_adbdump.o app_adbgen.o app_adbsign.o app_add.o app_audit.o app_cache.o \ app_convdb.o app_convndx.o app_del.o app_dot.o app_extract.o app_fetch.o \ app_fix.o app_index.o app_info.o app_list.o app_manifest.o app_mkndx.o \ - app_mkpkg.o app_policy.o app_update.o app_upgrade.o app_search.o \ - app_stats.o app_verify.o app_version.o applet.o + app_mkpkg.o app_policy.o app_query.o app_update.o app_upgrade.o \ + app_search.o app_stats.o app_verify.o app_version.o applet.o \ + serialize_query.o LIBS_apk := -lapk LIBS_apk.so := -L$(obj) -lapk diff --git a/src/apk.c b/src/apk.c index ddc7838c..02e7ad21 100644 --- a/src/apk.c +++ b/src/apk.c @@ -54,6 +54,7 @@ static void version(struct apk_out *out, const char *prefix) OPT(OPT_GLOBAL_help, APK_OPT_SH("h") "help") \ OPT(OPT_GLOBAL_interactive, APK_OPT_SH("i") "interactive") \ OPT(OPT_GLOBAL_keys_dir, APK_OPT_ARG "keys-dir") \ + OPT(OPT_GLOBAL_legacy_info, APK_OPT_BOOL "legacy-info") \ OPT(OPT_GLOBAL_no_cache, "no-cache") \ OPT(OPT_GLOBAL_no_check_certificate, "no-check-certificate") \ OPT(OPT_GLOBAL_no_interactive, "no-interactive") \ @@ -201,6 +202,9 @@ static int optgroup_global_parse(struct apk_ctx *ac, int opt, const char *optarg case OPT_GLOBAL_print_arch: puts(APK_DEFAULT_ARCH); return -ESHUTDOWN; + case OPT_GLOBAL_legacy_info: + ac->legacy_info = APK_OPT_BOOL_VAL(optarg); + break; default: return -ENOTSUP; } @@ -247,39 +251,6 @@ static int optgroup_commit_parse(struct apk_ctx *ac, int opt, const char *optarg return 0; } -#define SOURCE_OPTIONS(OPT) \ - OPT(OPT_SOURCE_from, APK_OPT_ARG "from") - -APK_OPTIONS(optgroup_source_desc, SOURCE_OPTIONS); - -static int optgroup_source_parse(struct apk_ctx *ac, int opt, const char *optarg) -{ - const unsigned long all_flags = APK_OPENF_NO_SYS_REPOS | APK_OPENF_NO_INSTALLED_REPO | APK_OPENF_NO_INSTALLED; - unsigned long flags; - - switch (opt) { - case OPT_SOURCE_from: - if (strcmp(optarg, "none") == 0) { - flags = APK_OPENF_NO_SYS_REPOS | APK_OPENF_NO_INSTALLED_REPO | APK_OPENF_NO_INSTALLED; - } else if (strcmp(optarg, "repositories") == 0) { - flags = APK_OPENF_NO_INSTALLED_REPO | APK_OPENF_NO_INSTALLED; - } else if (strcmp(optarg, "installed") == 0) { - flags = APK_OPENF_NO_SYS_REPOS; - } else if (strcmp(optarg, "system") == 0) { - flags = 0; - } else - return -ENOTSUP; - - ac->open_flags &= ~all_flags; - ac->open_flags |= flags; - break; - default: - return -ENOTSUP; - } - return 0; -} - - #define GENERATION_OPTIONS(OPT) \ OPT(OPT_GENERATION_compression, APK_OPT_ARG APK_OPT_SH("c") "compression") \ OPT(OPT_GENERATION_sign_key, APK_OPT_ARG "sign-key") @@ -430,8 +401,9 @@ static void setup_automatic_flags(struct apk_ctx *ac) else ac->out.progress_char = "#"; - if (!isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO) || !isatty(STDIN_FILENO)) { + if (!isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO)) { ac->out.progress_disable = 1; + ac->legacy_info = 1; return; } @@ -519,7 +491,7 @@ static int parse_options(int argc, char **argv, struct apk_applet *applet, void if (applet) { if (applet->options_desc) add_options(&opts, applet->options_desc, 15); if (applet->optgroup_commit) add_options(&opts, optgroup_commit_desc, 2); - if (applet->optgroup_source) add_options(&opts, optgroup_source_desc, 3); + if (applet->optgroup_query) add_options(&opts, optgroup_query_desc, 3); if (applet->optgroup_generation) add_options(&opts, optgroup_generation_desc, 4); } @@ -529,7 +501,7 @@ static int parse_options(int argc, char **argv, struct apk_applet *applet, void switch (APK_OPTVAL_GROUPID(p)) { case 1: r = optgroup_global_parse(ac, APK_OPTVAL_OPTIONID(p), arg); break; case 2: r = optgroup_commit_parse(ac, APK_OPTVAL_OPTIONID(p), arg); break; - case 3: r = optgroup_source_parse(ac, APK_OPTVAL_OPTIONID(p), arg); break; + case 3: r = apk_query_parse_option(ac, APK_OPTVAL_OPTIONID(p), arg); break; case 4: r = optgroup_generation_parse(ac, APK_OPTVAL_OPTIONID(p), arg); break; case 15: r = applet->parse(ctx, ac, APK_OPTVAL_OPTIONID(p), arg); break; default: r = -EINVAL; @@ -599,9 +571,10 @@ int main(int argc, char **argv) applet = deduce_applet(argc, argv); if (applet != NULL) { - if (applet->context_size != 0) - applet_ctx = calloc(1, applet->context_size); + extern const struct apk_serializer_ops apk_serializer_query; + ctx.query.ser = &apk_serializer_query; ctx.open_flags = applet->open_flags; + if (applet->context_size) applet_ctx = calloc(1, applet->context_size); if (applet->parse) applet->parse(applet_ctx, &ctx, APK_OPTIONS_INIT, NULL); } diff --git a/src/apk_applet.h b/src/apk_applet.h index 45da5a6d..ea832874 100644 --- a/src/apk_applet.h +++ b/src/apk_applet.h @@ -27,6 +27,10 @@ enum { init_macro(__APK_OPT_ENUM) }; \ static const char var_name[] = init_macro(__APK_OPT_DESC); +#define APK_OPTIONS_EXT(var_name, init_macro) \ + enum { init_macro(__APK_OPT_ENUM) }; \ + const char var_name[] = init_macro(__APK_OPT_DESC); + #define APK_OPTIONS_INIT 0xffff00 struct apk_applet { @@ -37,7 +41,7 @@ struct apk_applet { unsigned short optgroup_commit : 1; unsigned short optgroup_generation : 1; - unsigned short optgroup_source : 1; + unsigned short optgroup_query : 1; unsigned short remove_empty_arguments : 1; unsigned short context_size; unsigned int open_flags; diff --git a/src/apk_context.h b/src/apk_context.h index 22001001..771b4dc4 100644 --- a/src/apk_context.h +++ b/src/apk_context.h @@ -13,6 +13,7 @@ #include "apk_io.h" #include "apk_crypto.h" #include "apk_balloc.h" +#include "apk_query.h" #include "adb.h" #define APK_SIMULATE BIT(0) @@ -85,12 +86,14 @@ struct apk_ctx { struct apk_trust trust; struct apk_id_cache id_cache; struct apk_database *db; + struct apk_query_spec query; int root_fd, dest_fd; unsigned int root_set : 1; unsigned int cache_dir_set : 1; unsigned int cache_packages : 1; unsigned int cache_predownload : 1; unsigned int keys_loaded : 1; + unsigned int legacy_info : 1; }; void apk_ctx_init(struct apk_ctx *ac); diff --git a/src/apk_database.h b/src/apk_database.h index 912df564..f881fd7b 100644 --- a/src/apk_database.h +++ b/src/apk_database.h @@ -23,6 +23,7 @@ struct apk_name; APK_ARRAY(apk_name_array, struct apk_name *); +int apk_name_array_qsort(const void *a, const void *b); struct apk_db_acl { mode_t mode; @@ -124,7 +125,6 @@ struct apk_name { struct apk_name_array *rdepends; struct apk_name_array *rinstall_if; unsigned is_dependency : 1; - unsigned auto_select_virtual: 1; unsigned solver_flags_set : 1; unsigned providers_sorted : 1; unsigned has_repository_providers : 1; @@ -284,6 +284,7 @@ bool apk_db_arch_compatible(struct apk_database *db, apk_blob_t *arch); static inline bool apk_db_pkg_available(const struct apk_database *db, const struct apk_package *pkg) { return (pkg->repos & db->available_repos) ? true : false; } +const struct apk_package *apk_db_pkg_upgradable(const struct apk_database *db, const struct apk_package *pkg); struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package_tmpl *tmpl); struct apk_package *apk_db_get_pkg(struct apk_database *db, struct apk_digest *id); struct apk_package *apk_db_get_pkg_by_name(struct apk_database *db, apk_blob_t filename, ssize_t file_size, apk_blob_t pkgname_spec); @@ -308,6 +309,7 @@ int apk_db_cache_foreach_item(struct apk_database *db, apk_cache_item_cb cb); int apk_db_install_pkg(struct apk_database *db, struct apk_package *oldpkg, struct apk_package *newpkg, struct apk_progress *prog); +struct apk_name_array *apk_db_sorted_names(struct apk_database *db); struct apk_package_array *apk_db_sorted_installed_packages(struct apk_database *db); typedef int (*apk_db_foreach_name_cb)(struct apk_database *db, const char *match, struct apk_name *name, void *ctx); @@ -317,18 +319,3 @@ int apk_db_foreach_matching_name(struct apk_database *db, struct apk_string_arra int apk_db_foreach_sorted_name(struct apk_database *db, struct apk_string_array *filter, apk_db_foreach_name_cb cb, void *ctx); - -typedef int (*apk_db_foreach_package_cb)(struct apk_database *db, const char *match, struct apk_package *pkg, void *ctx); - -int __apk_db_foreach_sorted_package(struct apk_database *db, struct apk_string_array *filter, - apk_db_foreach_package_cb cb, void *cb_ctx, int provides); - -static inline int apk_db_foreach_sorted_package(struct apk_database *db, struct apk_string_array *filter, - apk_db_foreach_package_cb cb, void *cb_ctx) { - return __apk_db_foreach_sorted_package(db, filter, cb, cb_ctx, 0); -} - -static inline int apk_db_foreach_sorted_providers(struct apk_database *db, struct apk_string_array *filter, - apk_db_foreach_package_cb cb, void *cb_ctx) { - return __apk_db_foreach_sorted_package(db, filter, cb, cb_ctx, 1); -} diff --git a/src/apk_package.h b/src/apk_package.h index c2c5c01e..c4113914 100644 --- a/src/apk_package.h +++ b/src/apk_package.h @@ -106,6 +106,7 @@ static inline apk_blob_t apk_pkg_digest_blob(const struct apk_package *pkg) { } APK_ARRAY(apk_package_array, struct apk_package *); +int apk_package_array_qsort(const void *a, const void *b); #define APK_PROVIDER_FROM_PACKAGE(pkg) (struct apk_provider){(pkg),(pkg)->version} #define APK_PROVIDER_FROM_PROVIDES(pkg,p) (struct apk_provider){(pkg),(p)->version} diff --git a/src/apk_query.h b/src/apk_query.h new file mode 100644 index 00000000..88453aea --- /dev/null +++ b/src/apk_query.h @@ -0,0 +1,120 @@ +/* apk_query.h - Alpine Package Keeper (APK) + * + * Copyright (C) 2025 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#pragma once +#include "apk_defines.h" + +struct apk_query_spec; +struct apk_ostream; +struct apk_serializer; +struct apk_string_array; +struct apk_package_array; +struct apk_ctx; +struct apk_database; + +enum { + APK_Q_FIELD_QUERY = 0, + APK_Q_FIELD_ERROR, + + // who-owns + APK_Q_FIELD_PATH_TARGET, + APK_Q_FIELD_OWNER, + + // package fields + APK_Q_FIELD_PACKAGE, + APK_Q_FIELD_NAME, + APK_Q_FIELD_VERSION, + APK_Q_FIELD_HASH, + APK_Q_FIELD_DESCRIPTION, + APK_Q_FIELD_ARCH, + APK_Q_FIELD_LICENSE, + APK_Q_FIELD_ORIGIN, + APK_Q_FIELD_MAINTAINER, + APK_Q_FIELD_URL, + APK_Q_FIELD_COMMIT, + APK_Q_FIELD_BUILD_TIME, + APK_Q_FIELD_INSTALLED_SIZE, + APK_Q_FIELD_FILE_SIZE, + APK_Q_FIELD_PROVIDER_PRIORITY, + APK_Q_FIELD_DEPENDS, + APK_Q_FIELD_PROVIDES, + APK_Q_FIELD_REPLACES, + APK_Q_FIELD_INSTALL_IF, + APK_Q_FIELD_LAYER, + + // installed package fields + APK_Q_FIELD_CONTENTS, + APK_Q_FIELD_TRIGGERS, + APK_Q_FIELD_SCRIPTS, + APK_Q_FIELD_REPLACES_PRIORITY, + + // synthetic/repositories fields + APK_Q_FIELD_REPOSITORIES, + APK_Q_FIELD_DOWNLOAD_URL, + APK_Q_FIELD_REVDEPS_PKGNAME, + APK_Q_FIELD_REVDEPS_ORIGIN, + APK_Q_FIELD_RINSTALL_IF, + APK_Q_FIELD_STATUS, +}; + +#define APK_Q_FIELDS_ALL (BIT(APK_Q_FIELD_STATUS+1)-1) +#define APK_Q_FIELDS_MATCHABLE \ + (BIT(APK_Q_FIELD_NAME) | BIT(APK_Q_FIELD_VERSION) | BIT(APK_Q_FIELD_PACKAGE) | \ + BIT(APK_Q_FIELD_DESCRIPTION) | BIT(APK_Q_FIELD_LICENSE) | BIT(APK_Q_FIELD_ORIGIN) | \ + BIT(APK_Q_FIELD_MAINTAINER) | BIT(APK_Q_FIELD_URL) | \ + BIT(APK_Q_FIELD_PROVIDES) | BIT(APK_Q_FIELD_DEPENDS) | BIT(APK_Q_FIELD_INSTALL_IF) | \ + BIT(APK_Q_FIELD_REPLACES) | BIT(APK_Q_FIELD_CONTENTS) | BIT(APK_Q_FIELD_OWNER)) +#define APK_Q_FIELDS_DEFAULT_QUERY (BIT(APK_Q_FIELD_QUERY) | BIT(APK_Q_FIELD_ERROR)) +#define APK_Q_FIELDS_DEFAULT_PKG \ + (APK_Q_FIELDS_DEFAULT_QUERY | BIT(APK_Q_FIELD_NAME) | BIT(APK_Q_FIELD_VERSION) | \ + BIT(APK_Q_FIELD_DESCRIPTION) | BIT(APK_Q_FIELD_ARCH) | BIT(APK_Q_FIELD_LICENSE) | \ + BIT(APK_Q_FIELD_ORIGIN) | BIT(APK_Q_FIELD_URL) | BIT(APK_Q_FIELD_FILE_SIZE)) +#define APK_Q_FIELDS_DEFAULT_IPKG (APK_Q_FIELDS_DEFAULT_PKG | BIT(APK_Q_FIELD_CONTENTS) | BIT(APK_Q_FIELD_STATUS)) + +struct apk_query_spec { + struct { + uint8_t recursive : 1; + uint8_t world : 1; + uint8_t search : 1; + uint8_t empty_matches_all : 1; + } mode; + struct { + uint8_t all_matches : 1; + uint8_t available : 1; + uint8_t installed : 1; + uint8_t orphaned : 1; + uint8_t upgradable : 1; + } filter; + uint64_t match; + uint64_t fields; + const struct apk_serializer_ops *ser; +}; + +struct apk_query_match { + apk_blob_t query; + apk_blob_t path_target; // who-owns + struct apk_name *name; // name, provider or dependency match + struct apk_package *pkg; +}; + +typedef int (*apk_query_match_cb)(void *pctx, struct apk_query_match *); + +uint64_t apk_query_fields(apk_blob_t field_list, uint64_t allowed_fields, struct apk_out *out); +apk_blob_t apk_query_field(int f); +apk_blob_t apk_query_printable_field(apk_blob_t f); +int apk_query_parse_option(struct apk_ctx *ac, int opt, const char *optarg); +extern const char optgroup_query_desc[]; + +int apk_package_serialize(struct apk_package *pkg, struct apk_database *db, uint64_t fields, struct apk_serializer *ser); +int apk_query_match_serialize(struct apk_query_match *qm, struct apk_database *db, uint64_t fields, struct apk_serializer *ser); + +int apk_query_who_owns(struct apk_database *db, const char *path, struct apk_query_match *qm, char *buf, size_t bufsz); +int apk_query_matches(struct apk_ctx *ac, struct apk_query_spec *qs, struct apk_string_array *args, apk_query_match_cb match, void *pctx); +int apk_query_packages(struct apk_ctx *ac, struct apk_query_spec *qs, struct apk_string_array *args, struct apk_package_array **pkgs); +int apk_query_run(struct apk_ctx *ac, struct apk_query_spec *q, struct apk_string_array *args, struct apk_serializer *ser); +int apk_query_main(struct apk_ctx *ac, struct apk_string_array *args); diff --git a/src/apk_serialize.h b/src/apk_serialize.h index f60ab8b5..481eb485 100644 --- a/src/apk_serialize.h +++ b/src/apk_serialize.h @@ -36,8 +36,9 @@ struct apk_serializer { struct apk_trust *trust; }; +const struct apk_serializer_ops *apk_serializer_lookup(const char *format); struct apk_serializer *_apk_serializer_init(const struct apk_serializer_ops *ops, struct apk_ostream *os, void *ctx); -#define apk_serializer_init_alloca(ops, os) _apk_serializer_init(ops, os, ops->context_size < 1024 ? alloca(ops->context_size) : NULL) +#define apk_serializer_init_alloca(ops, os) _apk_serializer_init(ops, os, (ops)->context_size < 1024 ? alloca((ops)->context_size) : NULL) void apk_serializer_cleanup(struct apk_serializer *ser); static inline int apk_ser_start_schema(struct apk_serializer *ser, uint32_t schema_id) { return ser->ops->start_object(ser, schema_id); } diff --git a/src/app_adbdump.c b/src/app_adbdump.c index c601a99a..d3926a4a 100644 --- a/src/app_adbdump.c +++ b/src/app_adbdump.c @@ -18,16 +18,9 @@ static int adbdump_parse_option(void *pctx, struct apk_ctx *ac, int opt, const c struct adbdump_ctx *ctx = pctx; switch (opt) { - case APK_OPTIONS_INIT: - ctx->ser = &apk_serializer_yaml; - break; case OPT_ADBDUMP_format: - if (strcmp(optarg, "json") == 0) - ctx->ser = &apk_serializer_json; - else if (strcmp(optarg, "yaml") == 0) - ctx->ser = &apk_serializer_yaml; - else - return -EINVAL; + ctx->ser = apk_serializer_lookup(optarg); + if (IS_ERR(ctx->ser)) return -EINVAL; break; default: return -ENOTSUP; @@ -41,6 +34,7 @@ static int adbdump_main(void *pctx, struct apk_ctx *ac, struct apk_string_array struct apk_out *out = &ac->out; int r; + if (!ctx->ser) ctx->ser = &apk_serializer_yaml; apk_array_foreach_item(arg, args) { r = adb_walk_adb( adb_decompress(apk_istream_from_file_mmap(AT_FDCWD, arg), NULL), diff --git a/src/app_dot.c b/src/app_dot.c index f89027fc..4db1f445 100644 --- a/src/app_dot.c +++ b/src/app_dot.c @@ -17,30 +17,28 @@ #define S_EVALUATING -2 struct dot_ctx { + struct apk_query_spec *qs; unsigned short not_empty : 1; unsigned short errors_only : 1; - unsigned short installed_only : 1; }; #define DOT_OPTIONS(OPT) \ - OPT(OPT_DOT_errors, "errors") \ - OPT(OPT_DOT_installed, "installed") + OPT(OPT_DOT_errors, "errors") APK_OPTIONS(dot_options_desc, DOT_OPTIONS); static int dot_parse_option(void *pctx, struct apk_ctx *ac, int opt, const char *optarg) { struct dot_ctx *ctx = (struct dot_ctx *) pctx; + struct apk_query_spec *qs = &ac->query; switch (opt) { + case APK_OPTIONS_INIT: + qs->mode.empty_matches_all = 1; + break; case OPT_DOT_errors: ctx->errors_only = 1; break; - case OPT_DOT_installed: - ctx->installed_only = 1; - ac->open_flags &= ~APK_OPENF_NO_INSTALLED; - ac->open_flags |= APK_OPENF_NO_SYS_REPOS; - break; default: return -ENOTSUP; } @@ -81,11 +79,9 @@ static void dump_broken_deps(struct dot_ctx *ctx, struct apk_package *pkg, const static int dump_pkg(struct dot_ctx *ctx, struct apk_package *pkg) { + struct apk_query_spec *qs = ctx->qs; int r, ret = 0; - if (ctx->installed_only && pkg->ipkg == NULL) - return 0; - if (pkg->state_int == S_EVALUATED) return 0; @@ -111,10 +107,8 @@ static int dump_pkg(struct dot_ctx *ctx, struct apk_package *pkg) } apk_array_foreach(p0, name->providers) { - if (ctx->installed_only && p0->pkg->ipkg == NULL) - continue; - if (!apk_dep_is_provided(pkg, dep, p0)) - continue; + if (qs->filter.installed && !p0->pkg->ipkg) continue; + if (!apk_dep_is_provided(pkg, dep, p0)) continue; r = dump_pkg(ctx, p0->pkg); ret += r; @@ -140,27 +134,24 @@ static int dump_pkg(struct dot_ctx *ctx, struct apk_package *pkg) return ret; } -static int dump(struct apk_database *db, const char *match, struct apk_name *name, void *pctx) +static int dot_match(void *pctx, struct apk_query_match *qm) { struct dot_ctx *ctx = pctx; - if (!name) return 0; - - apk_name_sorted_providers(name); - apk_array_foreach(p, name->providers) - dump_pkg(ctx, p->pkg); + if (qm->pkg) dump_pkg(ctx, qm->pkg); return 0; } static int dot_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args) { - struct apk_database *db = ac->db; struct dot_ctx *ctx = (struct dot_ctx *) pctx; + struct apk_query_spec *qs = &ac->query; - apk_db_foreach_matching_name(db, args, dump, pctx); - - if (!ctx->not_empty) - return 1; + ctx->qs = qs; + qs->match |= BIT(APK_Q_FIELD_NAME) | BIT(APK_Q_FIELD_PROVIDES); + qs->mode.empty_matches_all = 1; + apk_query_matches(ac, qs, args, dot_match, ctx); + if (!ctx->not_empty) return 1; printf("}\n"); return 0; @@ -170,7 +161,7 @@ static struct apk_applet apk_dot = { .name = "dot", .open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE | APK_OPENF_ALLOW_ARCH, .options_desc = dot_options_desc, - .optgroup_source = 1, + .optgroup_query = 1, .remove_empty_arguments = 1, .context_size = sizeof(struct dot_ctx), .parse = dot_parse_option, diff --git a/src/app_fetch.c b/src/app_fetch.c index 3d7a697b..1fc824e6 100644 --- a/src/app_fetch.c +++ b/src/app_fetch.c @@ -17,26 +17,22 @@ #include "apk_applet.h" #include "apk_database.h" #include "apk_extract.h" -#include "apk_io.h" -#include "apk_print.h" -#include "apk_solver.h" +#include "apk_query.h" -#define FETCH_RECURSIVE 0x01 -#define FETCH_STDOUT 0x02 -#define FETCH_LINK 0x04 -#define FETCH_URL 0x08 -#define FETCH_WORLD 0x10 +#define FETCH_STDOUT 0x01 +#define FETCH_LINK 0x02 +#define FETCH_URL 0x04 struct fetch_ctx { + struct apk_ctx *ac; unsigned int flags; int outdir_fd, errors; time_t built_after; apk_blob_t pkgname_spec; - struct apk_database *db; struct apk_progress prog; - unsigned long done_packages, total_packages; + struct apk_package_array *pkgs; + unsigned long done_packages; uint64_t done_bytes, total_bytes; - struct apk_dependency_array *world; }; static int cup(void) @@ -77,12 +73,10 @@ static int cup(void) OPT(OPT_FETCH_built_after, APK_OPT_ARG "built-after") \ OPT(OPT_FETCH_link, APK_OPT_SH("l") "link") \ OPT(OPT_FETCH_pkgname_spec, APK_OPT_ARG "pkgname-spec") \ - OPT(OPT_FETCH_recursive, APK_OPT_SH("R") "recursive") \ OPT(OPT_FETCH_output, APK_OPT_ARG APK_OPT_SH("o") "output") \ OPT(OPT_FETCH_simulate, "simulate") \ OPT(OPT_FETCH_stdout, APK_OPT_SH("s") "stdout") \ OPT(OPT_FETCH_url, "url") \ - OPT(OPT_FETCH_world, APK_OPT_SH("w") "world") \ APK_OPTIONS(fetch_options_desc, FETCH_OPTIONS); @@ -116,9 +110,6 @@ static int fetch_parse_option(void *ctx, struct apk_ctx *ac, int opt, const char case OPT_FETCH_pkgname_spec: fctx->pkgname_spec = APK_BLOB_STR(optarg); break; - case OPT_FETCH_recursive: - fctx->flags |= FETCH_RECURSIVE; - break; case OPT_FETCH_stdout: fctx->flags |= FETCH_STDOUT; break; @@ -131,20 +122,16 @@ static int fetch_parse_option(void *ctx, struct apk_ctx *ac, int opt, const char case OPT_FETCH_url: fctx->flags |= FETCH_URL; break; - case OPT_FETCH_world: - fctx->flags |= FETCH_WORLD | FETCH_RECURSIVE; - ac->open_flags &= ~APK_OPENF_NO_WORLD; - break; default: return -ENOTSUP; } return 0; } -static int fetch_package(struct apk_database *db, const char *match, struct apk_package *pkg, void *pctx) +static int fetch_package(struct fetch_ctx *ctx, struct apk_package *pkg) { - struct fetch_ctx *ctx = pctx; - struct apk_out *out = &db->ctx->out; + struct apk_out *out = &ctx->ac->out; + struct apk_database *db = ctx->ac->db; struct apk_istream *is; struct apk_ostream *os; struct apk_repository *repo; @@ -154,9 +141,6 @@ static int fetch_package(struct apk_database *db, const char *match, struct apk_ char pkg_url[PATH_MAX], filename[PATH_MAX]; int r, pkg_fd; - if (!pkg->marked) - return 0; - apk_progress_item_start(&ctx->prog, apk_progress_weight(ctx->done_bytes, ctx->done_packages), pkg->size); repo = apk_db_select_repo(db, pkg); @@ -221,98 +205,30 @@ done: return 0; } -static void mark_package(struct fetch_ctx *ctx, struct apk_package *pkg) -{ - if (pkg == NULL || pkg->marked) - return; - if (ctx->built_after && pkg->build_time && ctx->built_after >= pkg->build_time) - return; - ctx->total_bytes += pkg->size; - ctx->total_packages++; - pkg->marked = 1; -} - -static void mark_error(struct fetch_ctx *ctx, const char *match, struct apk_name *name) -{ - struct apk_out *out = &ctx->db->ctx->out; - - if (strchr(match, '*') != NULL) - return; - - apk_msg(out, "%s: unable to select package (or its dependencies)", name ? name->name : match); - ctx->errors++; -} - -static void mark_dep_flags(struct fetch_ctx *ctx, struct apk_dependency *dep) -{ - dep->name->auto_select_virtual = 1; - apk_deps_add(&ctx->world, dep); -} - -static int mark_name_flags(struct apk_database *db, const char *match, struct apk_name *name, void *pctx) +static int fetch_match_package(void *pctx, struct apk_query_match *qm) { - struct fetch_ctx *ctx = (struct fetch_ctx *) pctx; - struct apk_dependency dep = (struct apk_dependency) { - .name = name, - .version = &apk_atom_null, - .op = APK_DEPMASK_ANY, - }; - - if (!name) { - ctx->errors++; - mark_error(ctx, match, name); + struct fetch_ctx *ctx = pctx; + struct apk_out *out = &ctx->ac->out; + struct apk_package *pkg = qm->pkg; + + if (pkg == NULL) { + if (!apk_blob_contains(qm->query, APK_BLOB_STRLIT('*'))) { + apk_msg(out, BLOB_FMT ": unable to select package (or its dependencies)", + BLOB_PRINTF(qm->query)); + ctx->errors++; + } return 0; } - mark_dep_flags(ctx, &dep); - return 0; -} - -static void mark_names_recursive(struct apk_database *db, struct apk_string_array *args, void *pctx) -{ - struct fetch_ctx *ctx = (struct fetch_ctx *) pctx; - struct apk_changeset changeset = {}; - struct apk_change *change; - int r; - - apk_change_array_init(&changeset.changes); - r = apk_solver_solve(db, APK_SOLVERF_IGNORE_CONFLICT, ctx->world, &changeset); - if (r == 0) { - foreach_array_item(change, changeset.changes) - mark_package(ctx, change->new_pkg); - } else { - apk_solver_print_errors(db, &changeset, ctx->world); - ctx->errors++; - } - apk_change_array_free(&changeset.changes); -} - -static int mark_name(struct apk_database *db, const char *match, struct apk_name *name, void *ctx) -{ - struct apk_package *pkg = NULL; - struct apk_provider *p; - - if (!name) goto err; - - foreach_array_item(p, name->providers) { - if (pkg == NULL || - (p->pkg->name == name && pkg->name != name) || - apk_version_compare(*p->version, *pkg->version) == APK_VERSION_GREATER) - pkg = p->pkg; - } - - if (!pkg) goto err; - mark_package(ctx, pkg); - return 0; - -err: - mark_error(ctx, match, name); + if (ctx->built_after && pkg->build_time && ctx->built_after >= pkg->build_time) return 0; + ctx->total_bytes += pkg->size; + apk_package_array_add(&ctx->pkgs, pkg); return 0; } static int purge_package(void *pctx, int dirfd, const char *filename) { struct fetch_ctx *ctx = (struct fetch_ctx *) pctx; - struct apk_database *db = ctx->db; + struct apk_database *db = ctx->ac->db; struct apk_out *out = &db->ctx->out; struct apk_file_info fi; @@ -332,9 +248,8 @@ static int fetch_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a struct apk_out *out = &ac->out; struct apk_database *db = ac->db; struct fetch_ctx *ctx = (struct fetch_ctx *) pctx; - struct apk_dependency *dep; - ctx->db = db; + ctx->ac = ac; if (APK_BLOB_IS_NULL(ctx->pkgname_spec)) ctx->pkgname_spec = ac->default_pkgname_spec; if (ctx->flags & FETCH_STDOUT) { @@ -351,37 +266,28 @@ static int fetch_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a return 0; } - if (ctx->flags & FETCH_RECURSIVE) { - apk_dependency_array_init(&ctx->world); - foreach_array_item(dep, db->world) - mark_dep_flags(ctx, dep); - if (apk_array_len(args) != 0) - apk_db_foreach_matching_name(db, args, mark_name_flags, ctx); - if (ctx->errors == 0) - mark_names_recursive(db, args, ctx); - apk_dependency_array_free(&ctx->world); - } else { - if (apk_array_len(args) != 0) - apk_db_foreach_matching_name(db, args, mark_name, ctx); - } - if (!ctx->errors) { - apk_progress_start(&ctx->prog, &ac->out, "fetch", apk_progress_weight(ctx->total_bytes, ctx->total_packages)); - apk_db_foreach_sorted_package(db, NULL, fetch_package, ctx); + apk_package_array_init(&ctx->pkgs); + apk_query_matches(ac, &ac->query, args, fetch_match_package, ctx); + if (ctx->errors == 0) { + apk_array_qsort(ctx->pkgs, apk_package_array_qsort); + apk_progress_start(&ctx->prog, &ac->out, "fetch", apk_progress_weight(ctx->total_bytes, apk_array_len(ctx->pkgs))); + apk_array_foreach_item(pkg, ctx->pkgs) + fetch_package(ctx, pkg); apk_progress_end(&ctx->prog); - } - - /* Remove packages not matching download spec from the output directory */ - if (!ctx->errors && (db->ctx->flags & APK_PURGE) && - !(ctx->flags & FETCH_STDOUT) && ctx->outdir_fd > 0) - apk_dir_foreach_file(ctx->outdir_fd, purge_package, ctx); + /* Remove packages not matching download spec from the output directory */ + if (!ctx->errors && (db->ctx->flags & APK_PURGE) && + !(ctx->flags & FETCH_STDOUT) && ctx->outdir_fd > 0) + apk_dir_foreach_file(ctx->outdir_fd, purge_package, ctx); + } + apk_package_array_free(&ctx->pkgs); return ctx->errors; } static struct apk_applet apk_fetch = { .name = "fetch", .options_desc = fetch_options_desc, - .optgroup_source = 1, + .optgroup_query = 1, .open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE | APK_OPENF_ALLOW_ARCH, .context_size = sizeof(struct fetch_ctx), .parse = fetch_parse_option, diff --git a/src/app_index.c b/src/app_index.c index ed8af0f7..9896eb61 100644 --- a/src/app_index.c +++ b/src/app_index.c @@ -81,44 +81,33 @@ static int index_parse_option(void *ctx, struct apk_ctx *ac, int opt, const char return 0; } -struct index_writer { - struct apk_ostream *os; - int count; - unsigned short index_flags; -}; - -static int index_write_entry(struct apk_database *db, const char *match, struct apk_package *pkg, void *ctx) +static int index_write(struct index_ctx *ictx, struct apk_database *db, struct apk_ostream *os) { - struct index_writer *iw = ctx; - - switch (iw->index_flags & (APK_INDEXF_MERGE|APK_INDEXF_PRUNE_ORIGIN)) { - case APK_INDEXF_MERGE: - break; - case APK_INDEXF_MERGE|APK_INDEXF_PRUNE_ORIGIN: - if (!pkg->marked && pkg->origin) { - struct apk_name *n = apk_db_query_name(db, *pkg->origin); - if (n && n->state_int) return 0; + int count = 0; + + apk_array_foreach_item(name, apk_db_sorted_names(db)) { + apk_array_foreach(p, apk_name_sorted_providers(name)) { + struct apk_package *pkg = p->pkg; + if (name != pkg->name) continue; + + switch (ictx->index_flags & (APK_INDEXF_MERGE|APK_INDEXF_PRUNE_ORIGIN)) { + case APK_INDEXF_MERGE: + break; + case APK_INDEXF_MERGE|APK_INDEXF_PRUNE_ORIGIN: + if (!pkg->marked && pkg->origin) { + struct apk_name *n = apk_db_query_name(db, *pkg->origin); + if (n && n->state_int) continue; + } + break; + default: + if (!pkg->marked) continue; + break; + } + count++; + apk_pkg_write_index_entry(pkg, os); } - break; - default: - if (!pkg->marked) return 0; - break; } - - iw->count++; - return apk_pkg_write_index_entry(pkg, iw->os); -} - -static int index_write(struct index_ctx *ictx, struct apk_database *db, struct apk_ostream *os) -{ - struct index_writer iw = { - .index_flags = ictx->index_flags, - .os = os, - }; - - apk_db_foreach_sorted_package(db, NULL, index_write_entry, &iw); - - return iw.count; + return count; } static int index_read_file(struct apk_database *db, struct index_ctx *ictx) diff --git a/src/app_info.c b/src/app_info.c index 911c6b00..d976cd85 100644 --- a/src/app_info.c +++ b/src/app_info.c @@ -16,31 +16,17 @@ #include "apk_database.h" #include "apk_print.h" +extern const struct apk_serializer_ops apk_serializer_query; + struct info_ctx { struct apk_database *db; - void (*action)(struct info_ctx *ctx, struct apk_database *db, struct apk_string_array *args); - int subaction_mask; - int errors; + unsigned int who_owns : 1; + unsigned int exists_test : 1; }; static int verbosity = 0; -/* These need to stay in sync with the function pointer array in - * info_subaction() */ -#define APK_INFO_DESC 0x01 -#define APK_INFO_URL 0x02 -#define APK_INFO_SIZE 0x04 -#define APK_INFO_DEPENDS 0x08 -#define APK_INFO_PROVIDES 0x10 -#define APK_INFO_RDEPENDS 0x20 -#define APK_INFO_CONTENTS 0x40 -#define APK_INFO_TRIGGERS 0x80 -#define APK_INFO_INSTALL_IF 0x100 -#define APK_INFO_RINSTALL_IF 0x200 -#define APK_INFO_REPLACES 0x400 -#define APK_INFO_LICENSE 0x800 - -static void verbose_print_pkg(struct apk_package *pkg, int minimal_verbosity) +static void info_print_pkg_oneline(struct apk_package *pkg, int minimal_verbosity) { int v = min(verbosity, minimal_verbosity); if (pkg == NULL || v < 1) return; @@ -50,138 +36,92 @@ static void verbose_print_pkg(struct apk_package *pkg, int minimal_verbosity) printf("\n"); } -static void info_exists(struct info_ctx *ctx, struct apk_database *db, - struct apk_string_array *args) +static int info_exists(struct info_ctx *ctx, struct apk_database *db, struct apk_string_array *args) { struct apk_name *name; struct apk_dependency dep; - struct apk_provider *p; - char **parg; - int ok; + int ok, errors = 0; - foreach_array_item(parg, args) { - apk_blob_t b = APK_BLOB_STR(*parg); + apk_array_foreach_item(arg, args) { + apk_blob_t b = APK_BLOB_STR(arg); apk_blob_pull_dep(&b, db, &dep, true); - if (APK_BLOB_IS_NULL(b) || b.len > 0) - continue; + if (APK_BLOB_IS_NULL(b) || b.len > 0) continue; name = dep.name; - if (name == NULL) - continue; + if (name == NULL) continue; ok = apk_dep_is_provided(NULL, &dep, NULL); - foreach_array_item(p, name->providers) { + apk_array_foreach(p, name->providers) { if (!p->pkg->ipkg) continue; ok = apk_dep_is_provided(NULL, &dep, p); - if (ok) verbose_print_pkg(p->pkg, 0); + if (ok) info_print_pkg_oneline(p->pkg, 0); break; } - if (!ok) ctx->errors++; + if (!ok) errors++; } + return errors; } -static struct apk_package *get_owner(struct apk_database *db, apk_blob_t fn) -{ - struct apk_db_dir *dir; - - apk_blob_pull_blob_match(&fn, APK_BLOB_STRLIT("/")); - fn = apk_blob_trim_end(fn, '/'); - - dir = apk_db_dir_query(db, fn); - if (dir && dir->owner) return dir->owner->pkg; - return apk_db_get_file_owner(db, fn); -} - -static void info_who_owns(struct info_ctx *ctx, struct apk_database *db, - struct apk_string_array *args) +static int info_who_owns(struct info_ctx *ctx, struct apk_database *db, struct apk_string_array *args) { struct apk_out *out = &db->ctx->out; - struct apk_package *pkg; - struct apk_dependency_array *deps; - struct apk_dependency dep; - struct apk_ostream *os; - const char *via; - char **parg, fnbuf[PATH_MAX], buf[PATH_MAX]; - apk_blob_t fn; - ssize_t r; - - apk_dependency_array_init(&deps); - foreach_array_item(parg, args) { - if (*parg[0] != '/' && realpath(*parg, fnbuf)) - fn = APK_BLOB_STR(fnbuf); - else - fn = APK_BLOB_STR(*parg); - - via = ""; - - pkg = get_owner(db, fn); - if (pkg == NULL) { - r = readlinkat(db->root_fd, *parg, buf, sizeof(buf)); - if (r > 0 && r < PATH_MAX && buf[0] == '/') { - pkg = get_owner(db, APK_BLOB_PTR_LEN(buf, r)); - via = "symlink target "; - } + struct apk_query_spec *qs = &db->ctx->query; + struct apk_package_array *pkgs; + struct apk_serializer *ser = NULL; + struct apk_query_match qm; + char fnbuf[PATH_MAX], buf[PATH_MAX]; + int errors = 0; + + if (qs->ser != &apk_serializer_query) { + if (!qs->fields) qs->fields = BIT(APK_Q_FIELD_QUERY) | BIT(APK_Q_FIELD_PATH_TARGET) | BIT(APK_Q_FIELD_ERROR) | BIT(APK_Q_FIELD_NAME); + ser = apk_serializer_init_alloca(qs->ser, apk_ostream_to_fd(STDOUT_FILENO)); + if (IS_ERR(ser)) return PTR_ERR(ser); + apk_ser_start_array(ser, apk_array_len(args)); + } + apk_package_array_init(&pkgs); + apk_array_foreach_item(arg, args) { + char *fn = arg; + if (arg[0] != '/' && realpath(arg, fnbuf)) fn = fnbuf; + apk_query_who_owns(db, fn, &qm, buf, sizeof buf); + if (ser) { + apk_ser_start_object(ser); + apk_query_match_serialize(&qm, db, qs->fields, ser); + apk_ser_end(ser); + continue; } - - if (pkg == NULL) { - apk_err(out, BLOB_FMT ": Could not find owner package", - BLOB_PRINTF(fn)); - ctx->errors++; + if (!qm.pkg) { + apk_err(out, "%s: Could not find owner package", fn); + errors++; continue; } - - if (verbosity < 1) { - dep = (struct apk_dependency) { - .name = pkg->name, - .version = &apk_atom_null, - .op = APK_DEPMASK_ANY, - }; - apk_deps_add(&deps, &dep); - } else { - printf(BLOB_FMT " %sis owned by " PKG_VER_FMT "\n", - BLOB_PRINTF(fn), via, PKG_VER_PRINTF(pkg)); + if (verbosity >= 1) { + printf("%s %sis owned by " PKG_VER_FMT "\n", + fn, qm.path_target.ptr ? "symlink target " : "", + PKG_VER_PRINTF(qm.pkg)); + } else if (!qm.pkg->marked) { + qm.pkg->marked = 1; + apk_package_array_add(&pkgs, qm.pkg); } } - if (verbosity < 1 && apk_array_len(deps) != 0) { - os = apk_ostream_to_fd(STDOUT_FILENO); - if (!IS_ERR(os)) { - apk_deps_write(db, deps, os, APK_BLOB_PTR_LEN(" ", 1)); - apk_ostream_write(os, "\n", 1); - apk_ostream_close(os); - } + if (apk_array_len(pkgs) != 0) { + apk_array_qsort(pkgs, apk_package_array_qsort); + apk_array_foreach_item(pkg, pkgs) printf("%s\n", pkg->name->name); } - apk_dependency_array_free(&deps); -} - -static void info_print_description(struct apk_database *db, struct apk_package *pkg) -{ - if (verbosity > 1) - printf("%s: " BLOB_FMT, pkg->name->name, BLOB_PRINTF(*pkg->description)); - else - printf(PKG_VER_FMT " description:\n" BLOB_FMT "\n", - PKG_VER_PRINTF(pkg), - BLOB_PRINTF(*pkg->description)); -} - -static void info_print_url(struct apk_database *db, struct apk_package *pkg) -{ - if (verbosity > 1) - printf("%s: " BLOB_FMT, pkg->name->name, BLOB_PRINTF(*pkg->url)); - else - printf(PKG_VER_FMT " webpage:\n" BLOB_FMT "\n", - PKG_VER_PRINTF(pkg), - BLOB_PRINTF(*pkg->url)); + apk_package_array_free(&pkgs); + if (ser) { + apk_ser_end(ser); + apk_serializer_cleanup(ser); + } + return errors; } -static void info_print_license(struct apk_database *db, struct apk_package *pkg) +static void info_print_blob(struct apk_database *db, struct apk_package *pkg, const char *field, apk_blob_t value) { if (verbosity > 1) - printf("%s: " BLOB_FMT , pkg->name->name, BLOB_PRINTF(*pkg->license)); + printf("%s: " BLOB_FMT "\n", pkg->name->name, BLOB_PRINTF(value)); else - printf(PKG_VER_FMT " license:\n" BLOB_FMT "\n", - PKG_VER_PRINTF(pkg), - BLOB_PRINTF(*pkg->license)); + printf(PKG_VER_FMT " %s:\n" BLOB_FMT "\n\n", PKG_VER_PRINTF(pkg), field, BLOB_PRINTF(value)); } static void info_print_size(struct apk_database *db, struct apk_package *pkg) @@ -191,40 +131,28 @@ static void info_print_size(struct apk_database *db, struct apk_package *pkg) size_unit = apk_get_human_size(pkg->installed_size, &size); if (verbosity > 1) - printf("%s: %" PRIu64 " %s", pkg->name->name, size, size_unit); + printf("%s: %" PRIu64 " %s\n", pkg->name->name, size, size_unit); else - printf(PKG_VER_FMT " installed size:\n%" PRIu64 " %s\n", + printf(PKG_VER_FMT " installed size:\n%" PRIu64 " %s\n\n", PKG_VER_PRINTF(pkg), size, size_unit); } static void info_print_dep_array(struct apk_database *db, struct apk_package *pkg, struct apk_dependency_array *deps, const char *dep_text) { - struct apk_dependency *d; apk_blob_t separator = APK_BLOB_STR(verbosity > 1 ? " " : "\n"); char buf[256]; - if (verbosity == 1) - printf(PKG_VER_FMT " %s:\n", PKG_VER_PRINTF(pkg), dep_text); - if (verbosity > 1) - printf("%s: ", pkg->name->name); - foreach_array_item(d, deps) { + if (verbosity == 1) printf(PKG_VER_FMT " %s:\n", PKG_VER_PRINTF(pkg), dep_text); + if (verbosity > 1) printf("%s: ", pkg->name->name); + apk_array_foreach(d, deps) { apk_blob_t b = APK_BLOB_BUF(buf); apk_blob_push_dep(&b, db, d); apk_blob_push_blob(&b, separator); b = apk_blob_pushed(APK_BLOB_BUF(buf), b); fwrite(b.ptr, b.len, 1, stdout); } -} - -static void info_print_depends(struct apk_database *db, struct apk_package *pkg) -{ - info_print_dep_array(db, pkg, pkg->depends, "depends on"); -} - -static void info_print_provides(struct apk_database *db, struct apk_package *pkg) -{ - info_print_dep_array(db, pkg, pkg->provides, "provides"); + puts(""); } static void print_rdep_pkg(struct apk_package *pkg0, struct apk_dependency *dep0, struct apk_package *pkg, void *pctx) @@ -234,46 +162,35 @@ static void print_rdep_pkg(struct apk_package *pkg0, struct apk_dependency *dep0 static void info_print_required_by(struct apk_database *db, struct apk_package *pkg) { - if (verbosity == 1) - printf(PKG_VER_FMT " is required by:\n", PKG_VER_PRINTF(pkg)); - if (verbosity > 1) - printf("%s: ", pkg->name->name); + if (verbosity == 1) printf(PKG_VER_FMT " is required by:\n", PKG_VER_PRINTF(pkg)); + if (verbosity > 1) printf("%s: ", pkg->name->name); apk_pkg_foreach_reverse_dependency( pkg, APK_FOREACH_INSTALLED | APK_DEP_SATISFIES | apk_foreach_genid(), print_rdep_pkg, NULL); -} - -static void info_print_install_if(struct apk_database *db, struct apk_package *pkg) -{ - info_print_dep_array(db, pkg, pkg->install_if, "has auto-install rule"); + puts(""); } static void info_print_rinstall_if(struct apk_database *db, struct apk_package *pkg) { - struct apk_name **name0; struct apk_dependency *dep; char *separator = verbosity > 1 ? " " : "\n"; - if (verbosity == 1) - printf(PKG_VER_FMT " affects auto-installation of:\n", - PKG_VER_PRINTF(pkg)); - if (verbosity > 1) - printf("%s: ", pkg->name->name); + if (verbosity == 1) printf(PKG_VER_FMT " affects auto-installation of:\n", PKG_VER_PRINTF(pkg)); + if (verbosity > 1) printf("%s: ", pkg->name->name); - foreach_array_item(name0, pkg->name->rinstall_if) { + apk_array_foreach_item(name0, pkg->name->rinstall_if) { /* Check only the package that is installed, and that * it actually has this package in install_if. */ - struct apk_package *pkg0 = apk_pkg_get_installed(*name0); + struct apk_package *pkg0 = apk_pkg_get_installed(name0); if (pkg0 == NULL) continue; foreach_array_item(dep, pkg0->install_if) { if (dep->name != pkg->name) continue; - printf(PKG_VER_FMT "%s", - PKG_VER_PRINTF(pkg0), - separator); + printf(PKG_VER_FMT "%s", PKG_VER_PRINTF(pkg0), separator); break; } } + puts(""); } static void info_print_contents(struct apk_database *db, struct apk_package *pkg) @@ -283,9 +200,7 @@ static void info_print_contents(struct apk_database *db, struct apk_package *pkg struct apk_db_file *file; struct hlist_node *dc, *dn, *fc, *fn; - if (verbosity == 1) - printf(PKG_VER_FMT " contains:\n", - PKG_VER_PRINTF(pkg)); + if (verbosity == 1) printf(PKG_VER_FMT " contains:\n", PKG_VER_PRINTF(pkg)); hlist_for_each_entry_safe(diri, dc, dn, &ipkg->owned_dirs, pkg_dirs_list) { @@ -296,74 +211,45 @@ static void info_print_contents(struct apk_database *db, struct apk_package *pkg printf(DIR_FILE_FMT "\n", DIR_FILE_PRINTF(diri->dir, file)); } } + puts(""); } static void info_print_triggers(struct apk_database *db, struct apk_package *pkg) { struct apk_installed_package *ipkg = pkg->ipkg; - char **trigger; - if (verbosity == 1) - printf(PKG_VER_FMT " triggers:\n", - PKG_VER_PRINTF(pkg)); - - foreach_array_item(trigger, ipkg->triggers) { + if (verbosity == 1) printf(PKG_VER_FMT " triggers:\n", PKG_VER_PRINTF(pkg)); + apk_array_foreach_item(trigger, ipkg->triggers) { if (verbosity > 1) printf("%s: trigger ", pkg->name->name); - printf("%s\n", *trigger); + printf("%s\n", trigger); } + puts(""); } -static void info_print_replaces(struct apk_database *db, struct apk_package *pkg) -{ - info_print_dep_array(db, pkg, pkg->ipkg->replaces, "replaces"); -} - -static void info_subaction(struct info_ctx *ctx, struct apk_package *pkg) +static void info_subactions(struct info_ctx *ctx, struct apk_package *pkg) { - typedef void (*subaction_t)(struct apk_database *, struct apk_package *); - static subaction_t subactions[] = { - info_print_description, - info_print_url, - info_print_size, - info_print_depends, - info_print_provides, - info_print_required_by, - info_print_contents, - info_print_triggers, - info_print_install_if, - info_print_rinstall_if, - info_print_replaces, - info_print_license, - }; - const int requireipkg = - APK_INFO_CONTENTS | APK_INFO_TRIGGERS | APK_INFO_RDEPENDS | - APK_INFO_RINSTALL_IF | APK_INFO_REPLACES; - int i; - - for (i = 0; i < ARRAY_SIZE(subactions); i++) { - if (!(BIT(i) & ctx->subaction_mask)) - continue; - - if (pkg->ipkg == NULL && (BIT(i) & requireipkg)) - continue; - - subactions[i](ctx->db, pkg); - puts(""); - } -} - -static int print_name_info(struct apk_database *db, const char *match, struct apk_package *pkg, void *pctx) -{ - struct info_ctx *ctx = (struct info_ctx *) pctx; - - if (!pkg) { - ctx->errors++; - return 0; + struct apk_database *db = ctx->db; + uint64_t fields = db->ctx->query.fields; + if (!pkg->ipkg) { + const uint64_t installed_package_fields = + BIT(APK_Q_FIELD_CONTENTS) | BIT(APK_Q_FIELD_TRIGGERS) | + BIT(APK_Q_FIELD_REVDEPS_PKGNAME) | BIT(APK_Q_FIELD_RINSTALL_IF) | + BIT(APK_Q_FIELD_REPLACES); + fields &= ~installed_package_fields; } - - info_subaction(ctx, pkg); - return 0; + if (fields & BIT(APK_Q_FIELD_DESCRIPTION)) info_print_blob(db, pkg, "description", *pkg->description); + if (fields & BIT(APK_Q_FIELD_URL)) info_print_blob(db, pkg, "webpage", *pkg->url); + if (fields & BIT(APK_Q_FIELD_INSTALLED_SIZE)) info_print_size(db, pkg); + if (fields & BIT(APK_Q_FIELD_DEPENDS)) info_print_dep_array(db, pkg, pkg->depends, "depends on"); + if (fields & BIT(APK_Q_FIELD_PROVIDES)) info_print_dep_array(db, pkg, pkg->provides, "provides"); + if (fields & BIT(APK_Q_FIELD_REVDEPS_PKGNAME)) info_print_required_by(db, pkg); + if (fields & BIT(APK_Q_FIELD_CONTENTS)) info_print_contents(db, pkg); + if (fields & BIT(APK_Q_FIELD_TRIGGERS)) info_print_triggers(db, pkg); + if (fields & BIT(APK_Q_FIELD_INSTALL_IF)) info_print_dep_array(db, pkg, pkg->install_if, "has auto-install rule"); + if (fields & BIT(APK_Q_FIELD_RINSTALL_IF)) info_print_rinstall_if(db, pkg); + if (fields & BIT(APK_Q_FIELD_REPLACES)) info_print_dep_array(db, pkg, pkg->ipkg->replaces, "replaces"); + if (fields & BIT(APK_Q_FIELD_LICENSE)) info_print_blob(db, pkg, "license", *pkg->license); } #define INFO_OPTIONS(OPT) \ @@ -371,8 +257,9 @@ static int print_name_info(struct apk_database *db, const char *match, struct ap OPT(OPT_INFO_contents, APK_OPT_SH("L") "contents") \ OPT(OPT_INFO_depends, APK_OPT_SH("R") "depends") \ OPT(OPT_INFO_description, APK_OPT_SH("d") "description") \ + OPT(OPT_INFO_exists, APK_OPT_SH("e") "exists") \ OPT(OPT_INFO_install_if, "install-if") \ - OPT(OPT_INFO_installed, APK_OPT_SH("e") "installed") \ + OPT(OPT_INFO_installed, "installed") \ OPT(OPT_INFO_license, "license") \ OPT(OPT_INFO_provides, APK_OPT_SH("P") "provides") \ OPT(OPT_INFO_rdepends, APK_OPT_SH("r") "rdepends") \ @@ -388,55 +275,62 @@ APK_OPTIONS(info_options_desc, INFO_OPTIONS); static int info_parse_option(void *pctx, struct apk_ctx *ac, int opt, const char *optarg) { struct info_ctx *ctx = (struct info_ctx *) pctx; + struct apk_query_spec *qs = &ac->query; - ctx->action = NULL; + ctx->who_owns = ctx->exists_test = 0; switch (opt) { + case OPT_INFO_exists: case OPT_INFO_installed: - ctx->action = info_exists; + ctx->exists_test = 1; ac->open_flags |= APK_OPENF_NO_REPOS; break; case OPT_INFO_who_owns: - ctx->action = info_who_owns; + ctx->who_owns = 1; ac->open_flags |= APK_OPENF_NO_REPOS; break; case OPT_INFO_webpage: - ctx->subaction_mask |= APK_INFO_URL; + qs->fields |= BIT(APK_Q_FIELD_URL); break; case OPT_INFO_depends: - ctx->subaction_mask |= APK_INFO_DEPENDS; + qs->fields |= BIT(APK_Q_FIELD_DEPENDS); break; case OPT_INFO_provides: - ctx->subaction_mask |= APK_INFO_PROVIDES; + qs->fields |= BIT(APK_Q_FIELD_PROVIDES); break; case OPT_INFO_rdepends: - ctx->subaction_mask |= APK_INFO_RDEPENDS; + qs->fields |= BIT(APK_Q_FIELD_REVDEPS_PKGNAME); break; case OPT_INFO_install_if: - ctx->subaction_mask |= APK_INFO_INSTALL_IF; + qs->fields |= BIT(APK_Q_FIELD_INSTALL_IF); break; case OPT_INFO_rinstall_if: - ctx->subaction_mask |= APK_INFO_RINSTALL_IF; + qs->fields |= BIT(APK_Q_FIELD_RINSTALL_IF); break; case OPT_INFO_size: - ctx->subaction_mask |= APK_INFO_SIZE; + qs->fields |= BIT(APK_Q_FIELD_INSTALLED_SIZE); break; case OPT_INFO_description: - ctx->subaction_mask |= APK_INFO_DESC; + qs->fields |= BIT(APK_Q_FIELD_DESCRIPTION); break; case OPT_INFO_contents: - ctx->subaction_mask |= APK_INFO_CONTENTS; + qs->fields |= BIT(APK_Q_FIELD_CONTENTS); break; case OPT_INFO_triggers: - ctx->subaction_mask |= APK_INFO_TRIGGERS; + qs->fields |= BIT(APK_Q_FIELD_TRIGGERS); break; case OPT_INFO_replaces: - ctx->subaction_mask |= APK_INFO_REPLACES; + qs->fields |= BIT(APK_Q_FIELD_REPLACES); break; case OPT_INFO_license: - ctx->subaction_mask |= APK_INFO_LICENSE; + qs->fields |= BIT(APK_Q_FIELD_LICENSE); break; case OPT_INFO_all: - ctx->subaction_mask = 0xffffffff; + qs->fields |= BIT(APK_Q_FIELD_URL) | BIT(APK_Q_FIELD_DEPENDS) | + BIT(APK_Q_FIELD_PROVIDES) | BIT(APK_Q_FIELD_REVDEPS_PKGNAME) | + BIT(APK_Q_FIELD_INSTALL_IF) | BIT(APK_Q_FIELD_RINSTALL_IF) | + BIT(APK_Q_FIELD_INSTALLED_SIZE) | BIT(APK_Q_FIELD_DESCRIPTION) | + BIT(APK_Q_FIELD_CONTENTS) | BIT(APK_Q_FIELD_TRIGGERS) | + BIT(APK_Q_FIELD_REPLACES) | BIT(APK_Q_FIELD_LICENSE); break; default: return -ENOTSUP; @@ -448,32 +342,44 @@ static int info_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *arg { struct apk_out *out = &ac->out; struct apk_database *db = ac->db; + struct apk_query_spec *qs = &ac->query; struct info_ctx *ictx = (struct info_ctx *) ctx; + struct apk_package_array *pkgs; + int oneline = 0; verbosity = apk_out_verbosity(out); ictx->db = db; - if (ictx->subaction_mask == 0) - ictx->subaction_mask = APK_INFO_DESC | APK_INFO_URL | APK_INFO_SIZE; - if (ictx->action != NULL) { - ictx->action(ictx, db, args); - } else if (apk_array_len(args) > 0) { - /* Print info on given packages */ - apk_db_foreach_sorted_providers(db, args, print_name_info, ctx); - } else { - /* Print all installed packages */ - struct apk_package_array *pkgs = apk_db_sorted_installed_packages(db); - struct apk_package **ppkg; - foreach_array_item(ppkg, pkgs) - verbose_print_pkg(*ppkg, 1); - } - return ictx->errors; + if (ictx->who_owns) return info_who_owns(ctx, db, args); + if (ictx->exists_test) return info_exists(ctx, db, args); + + qs->filter.all_matches = 1; + if (apk_array_len(args) == 0) { + qs->filter.installed = 1; + qs->mode.empty_matches_all = 1; + oneline = 1; + } + if (!qs->fields) qs->fields = BIT(APK_Q_FIELD_NAME) | BIT(APK_Q_FIELD_VERSION) | + BIT(APK_Q_FIELD_DESCRIPTION) | BIT(APK_Q_FIELD_URL) | BIT(APK_Q_FIELD_INSTALLED_SIZE); + if (!qs->match) qs->match = BIT(APK_Q_FIELD_NAME) | BIT(APK_Q_FIELD_PROVIDES); + if (qs->ser == &apk_serializer_query && (oneline || ac->legacy_info)) { + apk_package_array_init(&pkgs); + int errors = apk_query_packages(ac, qs, args, &pkgs); + if (oneline) { + apk_array_foreach_item(pkg, pkgs) info_print_pkg_oneline(pkg, 1); + }else { + apk_array_foreach_item(pkg, pkgs) info_subactions(ctx, pkg); + } + apk_package_array_free(&pkgs); + return errors; + } + return apk_query_main(ac, args); } static struct apk_applet apk_info = { .name = "info", .options_desc = info_options_desc, - .optgroup_source = 1, + .optgroup_query = 1, .open_flags = APK_OPENF_READ | APK_OPENF_ALLOW_ARCH, .context_size = sizeof(struct info_ctx), .parse = info_parse_option, diff --git a/src/app_list.c b/src/app_list.c index 3107c3c9..cdb2715b 100644 --- a/src/app_list.c +++ b/src/app_list.c @@ -15,59 +15,52 @@ #include "apk_applet.h" #include "apk_package.h" #include "apk_database.h" +#include "apk_hash.h" #include "apk_print.h" -struct list_ctx { - int verbosity; - unsigned int installed : 1; - unsigned int orphaned : 1; - unsigned int available : 1; - unsigned int upgradable : 1; - unsigned int match_origin : 1; - unsigned int match_depends : 1; - unsigned int match_providers : 1; - unsigned int manifest : 1; - - struct apk_string_array *filters; +struct match { + struct apk_name *name; + struct apk_package *pkg; }; +APK_ARRAY(match_array, struct match *); -static int origin_matches(const struct list_ctx *ctx, const struct apk_package *pkg) -{ - char **pmatch; - - if (pkg->origin->len == 0) return 0; - - foreach_array_item(pmatch, ctx->filters) { - if (apk_blob_compare(APK_BLOB_STR(*pmatch), *pkg->origin) == 0) - return 1; - } - - return 0; -} +struct match_hash_item { + struct hlist_node hash_node; + struct match match; +}; -static int is_orphaned(const struct apk_database *db, const struct apk_name *name) +static apk_blob_t match_hash_get_key(apk_hash_item item) { - return name ? !name->has_repository_providers : 0; + struct match_hash_item *m = item; + return APK_BLOB_STRUCT(m->match); } -/* returns the currently installed package if 'pkg' is a newer and installable version */ -static const struct apk_package *is_upgradable(const struct apk_database *db, const struct apk_package *pkg) -{ - struct apk_name *name = pkg->name; - struct apk_package *ipkg; - unsigned short allowed_repos; +static struct apk_hash_ops match_ops = { + .node_offset = offsetof(struct match_hash_item, hash_node), + .get_key = match_hash_get_key, + .hash_key = apk_blob_hash, + .compare = apk_blob_compare, +}; - ipkg = apk_pkg_get_installed(name); - if (!ipkg) return NULL; +struct list_ctx { + struct apk_balloc *ba; + struct apk_hash hash; + struct match_array *matches; + int verbosity; + unsigned int match_providers : 1; + unsigned int match_depends : 1; + unsigned int manifest : 1; +}; - allowed_repos = db->repo_tags[ipkg->ipkg->repository_tag].allowed_repos; - if (!(pkg->repos & allowed_repos)) return NULL; +static void print_package(const struct apk_database *db, const struct apk_name *name, const struct apk_package *pkg, const struct list_ctx *ctx) +{ + if (ctx->match_providers) printf("<%s> ", name->name); - return apk_version_match(*ipkg->version, APK_VERSION_LESS, *pkg->version) ? ipkg : NULL; -} + if (ctx->manifest) { + printf("%s " BLOB_FMT "\n", pkg->name->name, BLOB_PRINTF(*pkg->version)); + return; + } -static void print_package(const struct apk_database *db, const struct apk_package *pkg, const struct list_ctx *ctx) -{ if (ctx->verbosity <= 0) { printf("%s\n", pkg->name->name); return; @@ -86,11 +79,10 @@ static void print_package(const struct apk_database *db, const struct apk_packag if (pkg->ipkg) printf(" [installed]"); else { - const struct apk_package *u = is_upgradable(db, pkg); + const struct apk_package *u = apk_db_pkg_upgradable(db, pkg); if (u != NULL) printf(" [upgradable from: " PKG_VER_FMT "]", PKG_VER_PRINTF(u)); } - if (ctx->verbosity > 1) { printf("\n " BLOB_FMT "\n", BLOB_PRINTF(*pkg->description)); if (ctx->verbosity > 2) @@ -100,104 +92,49 @@ static void print_package(const struct apk_database *db, const struct apk_packag printf("\n"); } -static void print_manifest(const struct apk_package *pkg, const struct list_ctx *ctx) -{ - printf("%s " BLOB_FMT "\n", pkg->name->name, BLOB_PRINTF(*pkg->version)); -} - -static void filter_package(const struct apk_database *db, const struct apk_package *pkg, const struct list_ctx *ctx, const struct apk_name *name) -{ - if (ctx->match_origin && !origin_matches(ctx, pkg)) return; - if (ctx->installed && !pkg->ipkg) return; - if (ctx->orphaned && !is_orphaned(db, pkg->name)) return; - if (ctx->available && !apk_db_pkg_available(db, pkg)) return; - if (ctx->upgradable && !is_upgradable(db, pkg)) return; - - if (ctx->match_providers) printf("<%s> ", name->name); - if (ctx->manifest) - print_manifest(pkg, ctx); - else - print_package(db, pkg, ctx); -} - -static void iterate_providers(const struct apk_database *db, const struct apk_name *name, const struct list_ctx *ctx) -{ - struct apk_provider *p; - - foreach_array_item(p, name->providers) { - if (!ctx->match_providers && p->pkg->name != name) - continue; - - filter_package(db, p->pkg, ctx, name); - } -} - -static int print_result(struct apk_database *db, const char *match, struct apk_name *name, void *pctx) -{ - struct list_ctx *ctx = pctx; - struct apk_name **pname; - - if (!name) return 0; - - apk_name_sorted_providers(name); - if (ctx->match_depends) { - foreach_array_item(pname, name->rdepends) - iterate_providers(db, *pname, ctx); - } else { - iterate_providers(db, name, ctx); - } - return 0; -} - #define LIST_OPTIONS(OPT) \ - OPT(OPT_LIST_available, APK_OPT_SH("a") "available") \ + OPT(OPT_LIST_available, APK_OPT_SH("a")) \ OPT(OPT_LIST_depends, APK_OPT_SH("d") "depends") \ - OPT(OPT_LIST_installed, APK_OPT_SH("I") "installed") \ + OPT(OPT_LIST_installed, APK_OPT_SH("I")) \ OPT(OPT_LIST_manifest, "manifest") \ OPT(OPT_LIST_origin, APK_OPT_SH("o") "origin") \ - OPT(OPT_LIST_orphaned, APK_OPT_SH("O") "orphaned") \ + OPT(OPT_LIST_orphaned, APK_OPT_SH("O")) \ OPT(OPT_LIST_providers, APK_OPT_SH("P") "providers") \ - OPT(OPT_LIST_upgradable, APK_OPT_SH("u") "upgradable") \ - OPT(OPT_LIST_upgradeable, "upgradeable") + OPT(OPT_LIST_upgradeable, APK_OPT_SH("u") "upgradeable") APK_OPTIONS(list_options_desc, LIST_OPTIONS); static int list_parse_option(void *pctx, struct apk_ctx *ac, int opt, const char *optarg) { struct list_ctx *ctx = pctx; + struct apk_query_spec *qs = &ac->query; switch (opt) { case OPT_LIST_available: - ctx->available = 1; - ctx->orphaned = 0; + qs->filter.available = 1; break; case OPT_LIST_depends: ctx->match_depends = 1; break; case OPT_LIST_installed: - ctx->installed = 1; + installed: + qs->filter.installed = 1; ac->open_flags |= APK_OPENF_NO_SYS_REPOS; break; case OPT_LIST_manifest: ctx->manifest = 1; - ctx->installed = 1; - break; + goto installed; case OPT_LIST_origin: - ctx->match_origin = 1; + qs->match = BIT(APK_Q_FIELD_ORIGIN); break; case OPT_LIST_orphaned: - ctx->installed = 1; - ctx->orphaned = 1; + qs->filter.orphaned = 1; break; case OPT_LIST_providers: ctx->match_providers = 1; break; - case OPT_LIST_upgradable: case OPT_LIST_upgradeable: - ctx->available = 1; - ctx->orphaned = 0; - ctx->installed = 0; - ctx->upgradable = 1; + qs->filter.upgradable = 1; break; default: return -ENOTSUP; @@ -206,19 +143,57 @@ static int list_parse_option(void *pctx, struct apk_ctx *ac, int opt, const char return 0; } +static int match_array_sort(const void *a, const void *b) +{ + const struct match *ma = *(const struct match **)a, *mb = *(const struct match **)b; + int r = apk_name_cmp_display(ma->name, mb->name); + if (r) return r; + return apk_pkg_cmp_display(ma->pkg, mb->pkg); +} + +static int list_match_cb(void *pctx, struct apk_query_match *qm) +{ + struct list_ctx *ctx = pctx; + struct match m = { .name = qm->name, .pkg = qm->pkg }; + + if (!m.pkg) return 0; + if (!m.name) m.name = m.pkg->name; + + unsigned long hash = apk_hash_from_key(&ctx->hash, APK_BLOB_STRUCT(m)); + if (apk_hash_get_hashed(&ctx->hash, APK_BLOB_STRUCT(m), hash) != NULL) return 0; + + struct match_hash_item *hi = apk_balloc_new(ctx->ba, struct match_hash_item); + hi->match = m; + apk_hash_insert_hashed(&ctx->hash, hi, hash); + match_array_add(&ctx->matches, &hi->match); + return 0; +} + static int list_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args) { struct apk_out *out = &ac->out; struct apk_database *db = ac->db; + struct apk_query_spec *qs = &ac->query; struct list_ctx *ctx = pctx; + ctx->ba = &ac->ba; ctx->verbosity = apk_out_verbosity(out); - ctx->filters = args; - if (ctx->match_origin) - args = NULL; + qs->mode.empty_matches_all = 1; + qs->filter.all_matches = 1; + if (!qs->match) { + if (ctx->match_depends) qs->match = BIT(APK_Q_FIELD_DEPENDS); + else if (ctx->match_providers) qs->match = BIT(APK_Q_FIELD_NAME) | BIT(APK_Q_FIELD_PROVIDES); + else qs->match = BIT(APK_Q_FIELD_NAME); + } - apk_db_foreach_sorted_name(db, args, print_result, ctx); + apk_hash_init(&ctx->hash, &match_ops, 100); + match_array_init(&ctx->matches); + apk_query_matches(ac, qs, args, list_match_cb, ctx); + apk_array_qsort(ctx->matches, match_array_sort); + apk_array_foreach_item(m, ctx->matches) print_package(db, m->name, m->pkg, ctx); + match_array_free(&ctx->matches); + apk_hash_free(&ctx->hash); return 0; } @@ -226,7 +201,7 @@ static struct apk_applet apk_list = { .name = "list", .open_flags = APK_OPENF_READ | APK_OPENF_ALLOW_ARCH, .options_desc = list_options_desc, - .optgroup_source = 1, + .optgroup_query = 1, .context_size = sizeof(struct list_ctx), .parse = list_parse_option, .main = list_main, diff --git a/src/app_policy.c b/src/app_policy.c index 3a6aa27d..1d9fb6a0 100644 --- a/src/app_policy.c +++ b/src/app_policy.c @@ -13,62 +13,64 @@ #include "apk_version.h" #include "apk_print.h" -static int print_policy(struct apk_database *db, const char *match, struct apk_name *name, void *ctx) +static int policy_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args) { - struct apk_out *out = &db->ctx->out; - struct apk_provider *p; - struct apk_repository *repo; - int i, j, num = 0; + struct apk_package_array *pkgs; + struct apk_name *name = NULL; + struct apk_out *out = &ac->out; + struct apk_database *db = ac->db; + int r; + + ac->query.filter.all_matches = 1; - if (!name) return 0; + apk_package_array_init(&pkgs); + r = apk_query_packages(ac, &ac->query, args, &pkgs); + if (r < 0) { + apk_err(out, "query failed: %s", apk_error_str(r)); + goto err; + } -/* -zlib1g policy: - 2.0: - @testing http://nl.alpinelinux.org/alpine/edge/testing - 1.7: - @edge http://nl.alpinelinux.org/alpine/edge/main - 1.2.3.5 (upgradeable): - http://nl.alpinelinux.org/alpine/v2.6/main - 1.2.3.4 (installed): - /media/cdrom/... - http://nl.alpinelinux.org/alpine/v2.5/main - 1.1: - http://nl.alpinelinux.org/alpine/v2.4/main -*/ - apk_name_sorted_providers(name); - foreach_array_item(p, name->providers) { - if (p->pkg->name != name) continue; - if (num++ == 0) apk_out(out, "%s policy:", name->name); - apk_out(out, " " BLOB_FMT ":", BLOB_PRINTF(*p->version)); - if (p->pkg->ipkg) - apk_out(out, " %s/installed", apk_db_layer_name(p->pkg->layer)); - for (i = 0; i < db->num_repos; i++) { - repo = &db->repos[i]; - if (!(BIT(i) & p->pkg->repos)) - continue; - for (j = 0; j < db->num_repo_tags; j++) { - if (db->repo_tags[j].allowed_repos & p->pkg->repos) + apk_array_foreach_item(pkg, pkgs) { + /* + zlib1g policy: + 2.0: + @testing http://nl.alpinelinux.org/alpine/edge/testing + 1.7: + @edge http://nl.alpinelinux.org/alpine/edge/main + 1.2.3.5 (upgradeable): + http://nl.alpinelinux.org/alpine/v2.6/main + 1.2.3.4 (installed): + /media/cdrom/... + http://nl.alpinelinux.org/alpine/v2.5/main + 1.1: + http://nl.alpinelinux.org/alpine/v2.4/main + */ + if (pkg->name != name) { + name = pkg->name; + apk_out(out, "%s policy:", name->name); + } + apk_out(out, " " BLOB_FMT ":", BLOB_PRINTF(*pkg->version)); + if (pkg->ipkg) apk_out(out, " %s/installed", apk_db_layer_name(pkg->layer)); + for (int i = 0; i < db->num_repos; i++) { + if (!(BIT(i) & pkg->repos)) continue; + for (int j = 0; j < db->num_repo_tags; j++) { + if (db->repo_tags[j].allowed_repos & pkg->repos) apk_out(out, " " BLOB_FMT "%s" BLOB_FMT, BLOB_PRINTF(db->repo_tags[j].tag), j == 0 ? "" : " ", - BLOB_PRINTF(repo->url_base_printable)); + BLOB_PRINTF(db->repos[i].url_base_printable)); } } } - return 0; -} - -static int policy_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args) -{ - if (apk_array_len(args) == 0) return 0; - apk_db_foreach_sorted_name(ac->db, args, print_policy, NULL); - return 0; + r = 0; +err: + apk_package_array_free(&pkgs); + return r; } static struct apk_applet apk_policy = { .name = "policy", - .optgroup_source = 1, + .optgroup_query = 1, .open_flags = APK_OPENF_READ | APK_OPENF_ALLOW_ARCH, .main = policy_main, }; diff --git a/src/app_query.c b/src/app_query.c new file mode 100644 index 00000000..08080202 --- /dev/null +++ b/src/app_query.c @@ -0,0 +1,26 @@ +/* app_query.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2025 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include <unistd.h> +#include "apk_database.h" +#include "apk_applet.h" +#include "apk_query.h" + +static int query_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args) +{ + return apk_query_main(ac, args); +} + +static struct apk_applet apk_query = { + .name = "query", + .optgroup_query = 1, + .open_flags = APK_OPENF_READ | APK_OPENF_ALLOW_ARCH, + .main = query_main, +}; + +APK_DEFINE_APPLET(apk_query); diff --git a/src/app_search.c b/src/app_search.c index db0c7db4..664d7a3d 100644 --- a/src/app_search.c +++ b/src/app_search.c @@ -19,14 +19,8 @@ struct search_ctx { void (*print_package)(struct search_ctx *ctx, struct apk_package *pkg); int verbosity; - unsigned int show_all : 1; - unsigned int search_exact : 1; - unsigned int search_description : 1; - unsigned int search_origin : 1; - unsigned int matches; struct apk_string_array *filter; - struct apk_package *prev_match; }; static void print_package_name(struct search_ctx *ctx, struct apk_package *pkg) @@ -78,17 +72,24 @@ APK_OPTIONS(search_options_desc, SEARCH_OPTIONS); static int search_parse_option(void *ctx, struct apk_ctx *ac, int opt, const char *optarg) { struct search_ctx *ictx = (struct search_ctx *) ctx; + struct apk_query_spec *qs = &ac->query; switch (opt) { + case APK_OPTIONS_INIT: + qs->mode.search = 1; + qs->mode.empty_matches_all = 1; + //qs->match = BIT(APK_Q_FIELD_NAME) | BIT(APK_Q_FIELD_PROVIDES); + break; case OPT_SEARCH_all: - ictx->show_all = 1; + qs->filter.all_matches = 1; break; case OPT_SEARCH_description: - ictx->search_description = 1; - ictx->show_all = 1; + qs->match = BIT(APK_Q_FIELD_NAME) | BIT(APK_Q_FIELD_DESCRIPTION); + qs->mode.search = 1; + qs->filter.all_matches = 1; break; case OPT_SEARCH_exact: - ictx->search_exact = 1; + qs->mode.search = 0; break; case OPT_SEARCH_origin: ictx->print_package = print_origin_name; @@ -97,9 +98,9 @@ static int search_parse_option(void *ctx, struct apk_ctx *ac, int opt, const cha ictx->print_result = print_rdepends; break; case OPT_SEARCH_has_origin: - ictx->search_origin = 1; - ictx->search_exact = 1; - ictx->show_all = 1; + qs->match = BIT(APK_Q_FIELD_ORIGIN); + qs->filter.all_matches = 1; + qs->mode.search = 0; break; default: return -ENOTSUP; @@ -107,59 +108,13 @@ static int search_parse_option(void *ctx, struct apk_ctx *ac, int opt, const cha return 0; } -static void print_result_pkg(struct search_ctx *ctx, struct apk_package *pkg) -{ - char buf[2048]; - - if (ctx->search_description) { - apk_array_foreach_item(match, ctx->filter) { - if (fnmatch(match, pkg->name->name, FNM_CASEFOLD) == 0) goto match; - if (apk_fmt(buf, sizeof buf, BLOB_FMT, BLOB_PRINTF(*pkg->description)) > 0 && - fnmatch(match, buf, FNM_CASEFOLD) == 0) goto match; - } - return; - } - if (ctx->search_origin) { - apk_array_foreach_item(match, ctx->filter) { - if (!pkg->origin) continue; - if (apk_blob_compare(APK_BLOB_STR(match), *pkg->origin) == 0) - goto match; - } - return; - } -match: - ctx->print_result(ctx, pkg); -} - -static int print_result(struct apk_database *db, const char *match, struct apk_package *pkg, void *pctx) -{ - struct search_ctx *ctx = pctx; - - if (!pkg) return 0; - - if (ctx->show_all) { - print_result_pkg(ctx, pkg); - return 0; - } - - if (!ctx->prev_match) { - ctx->prev_match = pkg; - return 0; - } - if (ctx->prev_match->name != pkg->name) { - print_result_pkg(ctx, ctx->prev_match); - ctx->prev_match = pkg; - return 0; - } - if (apk_pkg_version_compare(pkg, ctx->prev_match) == APK_VERSION_GREATER) - ctx->prev_match = pkg; - return 0; -} - static int search_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args) { struct apk_database *db = ac->db; + struct apk_out *out = &ac->out; struct search_ctx *ctx = (struct search_ctx *) pctx; + struct apk_package_array *pkgs; + int r; ctx->verbosity = apk_out_verbosity(&db->ctx->out); ctx->filter = args; @@ -169,28 +124,23 @@ static int search_main(void *pctx, struct apk_ctx *ac, struct apk_string_array * if (ctx->print_result == NULL) ctx->print_result = ctx->print_package; - if (ctx->search_description || ctx->search_origin) { - // Just enumerate all names in sorted order, and do the - // filtering in the callback. - args = NULL; + ac->query.match |= BIT(APK_Q_FIELD_NAME) | BIT(APK_Q_FIELD_PROVIDES); + apk_package_array_init(&pkgs); + r = apk_query_packages(ac, &ac->query, args, &pkgs); + if (r == 0) { + apk_array_foreach_item(pkg, pkgs) ctx->print_result(ctx, pkg); + } else { + apk_err(out, "query failed: %s", apk_error_str(r)); } + apk_package_array_free(&pkgs); - if (!ctx->search_exact) { - apk_array_foreach(pmatch, ctx->filter) { - size_t slen = strlen(*pmatch) + 3; - *pmatch = apk_fmts(alloca(slen), slen, "*%s*", *pmatch); - } - } - apk_db_foreach_sorted_providers(db, args, print_result, ctx); - if (ctx->prev_match) print_result_pkg(ctx, ctx->prev_match); - - return 0; + return r; } static struct apk_applet apk_search = { .name = "search", .options_desc = search_options_desc, - .optgroup_source = 1, + .optgroup_query = 1, .open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE | APK_OPENF_ALLOW_ARCH, .context_size = sizeof(struct search_ctx), .parse = search_parse_option, diff --git a/src/applet.c b/src/applet.c index c6c6701a..af5b6f92 100644 --- a/src/applet.c +++ b/src/applet.c @@ -35,12 +35,12 @@ struct apk_applet *apk_applet_find(const char *name) #ifndef NO_HELP static inline int is_group(struct apk_applet *applet, const char *topic) { - if (!applet) return strcasecmp(topic, "apk") == 0; - if (strcasecmp(topic, applet->name) == 0) return 1; - if (strcasecmp(topic, "Global") == 0) return 1; - if (applet->optgroup_generation && strcasecmp(topic, "Generation") == 0) return 1; - if (applet->optgroup_commit && strcasecmp(topic, "Commit") == 0) return 1; - if (applet->optgroup_source && strcasecmp(topic, "Source") == 0) return 1; + if (!applet) return strcmp(topic, "APK") == 0; + if (strcmp(topic, applet->name) == 0) return 1; + if (strcmp(topic, "GLOBAL") == 0) return 1; + if (applet->optgroup_generation && strcmp(topic, "GENERATION") == 0) return 1; + if (applet->optgroup_commit && strcmp(topic, "COMMIT") == 0) return 1; + if (applet->optgroup_query && strcmp(topic, "QUERY") == 0) return 1; return 0; } #endif diff --git a/src/database.c b/src/database.c index 272abb7b..53f2c3fd 100644 --- a/src/database.c +++ b/src/database.c @@ -593,6 +593,19 @@ bool apk_db_arch_compatible(struct apk_database *db, apk_blob_t *arch) return db->noarch == arch; } +const struct apk_package *apk_db_pkg_upgradable(const struct apk_database *db, const struct apk_package *pkg) +{ + struct apk_name *name = pkg->name; + struct apk_package *ipkg = apk_pkg_get_installed(name); + + if (!ipkg) return NULL; + + unsigned short allowed_repos = db->repo_tags[ipkg->ipkg->repository_tag].allowed_repos; + if (!(pkg->repos & allowed_repos)) return NULL; + + return apk_version_match(*ipkg->version, APK_VERSION_LESS, *pkg->version) ? ipkg : NULL; +} + struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package_tmpl *tmpl) { struct apk_package *pkg = &tmpl->pkg, *idb; @@ -3180,18 +3193,6 @@ static int apk_name_match(struct apk_name *name, struct apk_string_array *filter return apk_string_match(name->name, filter, res); } -static int apk_pkg_match(struct apk_package *pkg, struct apk_string_array *filter, const char **res, int provides) -{ - struct apk_dependency *d; - - if (apk_name_match(pkg->name, filter, res)) return 1; - if (!provides) return 0; - foreach_array_item(d, pkg->provides) { - if (apk_string_match(d->name->name, filter, res)) return 1; - } - return 0; -} - static int match_names(apk_hash_item item, void *pctx) { struct match_ctx *ctx = (struct match_ctx *) pctx; @@ -3234,13 +3235,13 @@ all: return apk_hash_foreach(&db->available.names, match_names, &mctx); } -static int cmp_name(const void *a, const void *b) +int apk_name_array_qsort(const void *a, const void *b) { const struct apk_name * const* na = a, * const* nb = b; return apk_name_cmp_display(*na, *nb); } -static int cmp_package(const void *a, const void *b) +int apk_package_array_qsort(const void *a, const void *b) { const struct apk_package * const* pa = a, * const* pb = b; return apk_pkg_cmp_display(*pa, *pb); @@ -3253,12 +3254,12 @@ static int add_name(apk_hash_item item, void *ctx) return 0; } -static struct apk_name_array *apk_db_sorted_names(struct apk_database *db) +struct apk_name_array *apk_db_sorted_names(struct apk_database *db) { if (!db->sorted_names) { apk_name_array_resize(&db->available.sorted_names, 0, db->available.names.num_items); apk_hash_foreach(&db->available.names, add_name, &db->available.sorted_names); - apk_array_qsort(db->available.sorted_names, cmp_name); + apk_array_qsort(db->available.sorted_names, apk_name_array_qsort); db->sorted_names = 1; } return db->available.sorted_names; @@ -3273,7 +3274,7 @@ struct apk_package_array *apk_db_sorted_installed_packages(struct apk_database * apk_package_array_resize(&db->installed.sorted_packages, 0, db->installed.stats.packages); list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) apk_package_array_add(&db->installed.sorted_packages, ipkg->pkg); - apk_array_qsort(db->installed.sorted_packages, cmp_package); + apk_array_qsort(db->installed.sorted_packages, apk_package_array_qsort); } return db->installed.sorted_packages; } @@ -3316,7 +3317,7 @@ int apk_db_foreach_sorted_name(struct apk_database *db, struct apk_string_array res = a->item; num_res = apk_array_len(a); } else { - qsort(results, num_res, sizeof results[0], cmp_name); + qsort(results, num_res, sizeof results[0], apk_name_array_qsort); res = results; } @@ -3329,71 +3330,3 @@ int apk_db_foreach_sorted_name(struct apk_database *db, struct apk_string_array } return 0; } - -int __apk_db_foreach_sorted_package(struct apk_database *db, struct apk_string_array *filter, - apk_db_foreach_package_cb cb, void *cb_ctx, int provides) -{ - char **pmatch; - const char *match; - struct apk_name *name; - struct apk_package *results[128]; - struct apk_provider *p; - bool walk_all = false; - size_t i, num_res = 0; - int r; - - if (!filter || apk_array_len(filter) == 0) { - filter = NULL; - walk_all = true; - } else { - foreach_array_item(pmatch, filter) { - bool no_match = true; - if (strchr(*pmatch, '*')) { - walk_all = true; - continue; - } - name = apk_hash_get(&db->available.names, APK_BLOB_STR(*pmatch)); - if (name) { - foreach_array_item(p, name->providers) { - if (!provides && p->pkg->name != name) continue; - no_match = false; - if (walk_all) break; - if (p->pkg->seen) continue; - if (num_res >= ARRAY_SIZE(results)) { - walk_all = true; - break; - } - p->pkg->seen = 1; - results[num_res++] = p->pkg; - } - } - if (no_match) cb(db, *pmatch, NULL, cb_ctx); - } - for (i = 0; i < num_res; i++) results[i]->seen = 0; - } - - if (walk_all) { - struct apk_name_array *name_array = apk_db_sorted_names(db); - struct apk_name **nameptr; - foreach_array_item(nameptr, name_array) { - name = *nameptr; - apk_name_sorted_providers(name); - foreach_array_item(p, name->providers) { - if (p->pkg->name != name) continue; - if (apk_pkg_match(p->pkg, filter, &match, provides)) { - r = cb(db, match, p->pkg, cb_ctx); - if (r) return r; - } - } - } - } else { - qsort(results, num_res, sizeof results[0], cmp_package); - for (i = 0; i < num_res; i++) { - if (apk_pkg_match(results[i], filter, &match, provides)) { - r = cb(db, match, results[i], cb_ctx); - if (r) return r; - } - } - } - return 0; -} diff --git a/src/meson.build b/src/meson.build index 898ba4c5..3d0c1852 100644 --- a/src/meson.build +++ b/src/meson.build @@ -29,6 +29,7 @@ libapk_src = [ 'pathbuilder.c', 'print.c', 'process.c', + 'query.c', 'repoparser.c', 'serialize.c', 'serialize_json.c', @@ -59,6 +60,7 @@ libapk_headers = [ 'apk_pathbuilder.h', 'apk_print.h', 'apk_provider_data.h', + 'apk_query.h', 'apk_serialize.h', 'apk_solver_data.h', 'apk_solver.h', @@ -89,6 +91,7 @@ apk_src = [ 'app_mkndx.c', 'app_mkpkg.c', 'app_policy.c', + 'app_query.c', 'app_update.c', 'app_upgrade.c', 'app_search.c', @@ -96,6 +99,7 @@ apk_src = [ 'app_verify.c', 'app_version.c', 'applet.c', + 'serialize_query.c', ] apk_cargs = [ diff --git a/src/serialize.c b/src/serialize.c index c8f43a7a..381f4248 100644 --- a/src/serialize.c +++ b/src/serialize.c @@ -3,6 +3,14 @@ #include "apk_serialize.h" #include "apk_io.h" +const struct apk_serializer_ops *apk_serializer_lookup(const char *format) +{ + if (strcmp(format, "json") == 0) return &apk_serializer_json; + if (strcmp(format, "yaml") == 0) return &apk_serializer_yaml; + if (strcmp(format, "default") == 0) return NULL; + return ERR_PTR(-EINVAL); +} + struct apk_serializer *_apk_serializer_init(const struct apk_serializer_ops *ops, struct apk_ostream *os, void *ctx) { int r = -ENOMEM; diff --git a/src/serialize_yaml.c b/src/serialize_yaml.c index 4d895aff..f1724ec4 100644 --- a/src/serialize_yaml.c +++ b/src/serialize_yaml.c @@ -13,7 +13,7 @@ struct serialize_yaml { static void ser_yaml_indent(struct serialize_yaml *dt, bool item, bool continue_line) { - static char pad[] = " "; + char pad[] = " "; if (!dt->line_started) { assert(sizeof pad >= 2*dt->indent); diff --git a/src/solver.c b/src/solver.c index 984c58ea..33ea803f 100644 --- a/src/solver.c +++ b/src/solver.c @@ -361,11 +361,9 @@ static inline int merge_index_complete(unsigned short *index, int num_options) static bool is_provider_auto_selectable(struct apk_provider *p) { - /* Virtual packages without provider_priority cannot be autoselected, - * without provider_priority or auto_select_virtual override */ + // Virtual packages without provider_priority cannot be autoselected without provider_priority if (p->version != &apk_atom_null) return true; if (p->pkg->provider_priority) return true; - if (p->pkg->name->auto_select_virtual) return true; if (p->pkg->name->ss.requirers) return true; return false; } -- GitLab