diff --git a/doc/Makefile b/doc/Makefile index 400de206038ee67b2b560a8fcf5bd80954bfb79d..49d8ff8fdd8af603b8e23f1d2b6f1e0b47509dc7 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 bbd703bd0d205f25cd437624f44729fcf0dc38f4..1bee547ef9433f89acaf1a2be22314763f6f3a67 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 0000000000000000000000000000000000000000..1e891a41e168beab4acf46aaf7cb82e20308b29a --- /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 4fc101be0d2f3f97ac48faf23bbb7354fe595b9a..0a1a9c74389598a8bd013340ce86ffe69a5abb73 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 2b52e4e7fcfce0ab742f6299797fb00eec8a89ac..b85091ee31918c49f83737919d376827fabe3841 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 8fc89c1b2f7a00bdfd74e8a7ee256dfd9d8a08a6..db8d5f85b26df830c03072aa2f176f127146001e 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 ddc7838ce0082ef626f55fe907bf9a144fd1ced3..02e7ad218fa535fd7d995888dfba2b836f46facd 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 45da5a6da779846b3a9972a88a33459a236b74ce..ea83287484915c413b7a7fe7ba1eee1a7403a431 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 220010015ec1ddda5b28b99f5c9d5b3a788cb398..771b4dc4227cc4f8367c1e1b7b7c4dc49a7eb9a7 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 912df5649ed49a27f9cd11539e80c7b6cb9f34e0..f881fd7bcffe14db46bab0714f988584850bf601 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 c2c5c01e7d7acf672d016745cd47d137947ec648..c411391451c7d4d23f73237baca3b698a58a6026 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 0000000000000000000000000000000000000000..88453aeae06b82953ad4d675afd0cfa787a16ef5 --- /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 f60ab8b590fd7f209d7845089a5c21db1cf5b6b1..481eb48520c77b68e30e10555f0219fc4d649585 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 c601a99a2102ee64322a8acc32bd6c96f6a09d79..d3926a4a3dffd470102946c00eedc684c06e8223 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 f89027fc23fd2ae4110ecc9b0b1c4217489ab19c..4db1f44542db7c33c6bffdde031795ff44c2616c 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 3d7a697b67ec206ffe8584ec979edf2d56a31705..1fc824e6bf32ed8aced214c313555331e54f2155 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 ed8af0f7fd019e23560856c9ac19bc0f7d8b55d6..9896eb61ae0769caacaddacea698197ef8595b2c 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 911c6b0014f67b5417e83b67b89cd4f65975198c..d976cd857c50ab49b16cd40a3a4ac4cf9473d793 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 3107c3c98bd5aaa0494b1cafd627d30ddb763c1b..cdb2715bc18010bbfb03b76ebda0f506fb2446d2 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 3a6aa27d16254c28b6a763538127aee042733a8f..1d9fb6a04ccfa745c196e75551fc01262e521402 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 0000000000000000000000000000000000000000..080802026aea86ae22520311942138f90ad3411a --- /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 db0c7db4d586d50c26be07b243927d6a38b67ffc..664d7a3d885b50f259f726d6fa4863fd34ae26a9 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 c6c6701a9b8486e372d823d1279fe20a4f3aa269..af5b6f92de235858374d693edeca18a79cbdd61e 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 272abb7b6a2e6c858154883321604971cad72915..53f2c3fd5c0ed97df3f50084d1ba692c9ef0ef7f 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 898ba4c5b153c65c1dbd64febe8dfce21733ee7c..3d0c1852a7a3104facf36c07ccd3f514f55346e9 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 c8f43a7a4af0c4f7c273b7242ebed99728b89f08..381f424815275c4112465cc1165bcf95c6781ebf 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 4d895affa4b83c4abf3b0d455d70b8928d91504a..f1724ec4387ffeff9063f35e273786175893c451 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 984c58ea36cbbcdeccdd45bd4cac145cccade87a..33ea803fd856919184edf346750b63226f41f889 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; }