summaryrefslogtreecommitdiff
path: root/src/app_list.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/app_list.c')
-rw-r--r--src/app_list.c272
1 files changed, 272 insertions, 0 deletions
diff --git a/src/app_list.c b/src/app_list.c
new file mode 100644
index 0000000..3315ea3
--- /dev/null
+++ b/src/app_list.c
@@ -0,0 +1,272 @@
+/* app_list.c - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2005-2009 Natanael Copa <n@tanael.org>
+ * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
+ * Copyright (C) 2018 William Pitcock <nenolod@dereferenced.org>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation. See http://www.gnu.org/ for details.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include "apk_defines.h"
+#include "apk_applet.h"
+#include "apk_package.h"
+#include "apk_database.h"
+#include "apk_print.h"
+
+struct list_ctx {
+ 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;
+
+ struct apk_string_array *filters;
+};
+
+static int origin_matches(const struct list_ctx *ctx, const struct apk_package *pkg)
+{
+ char **pmatch;
+
+ if (pkg->origin == NULL)
+ return 0;
+
+ foreach_array_item(pmatch, ctx->filters)
+ {
+ if (apk_blob_compare(APK_BLOB_STR(*pmatch), *pkg->origin) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int is_orphaned(const struct apk_name *name)
+{
+ struct apk_provider *p;
+ unsigned int repos = 0;
+
+ if (name == NULL)
+ return 0;
+
+ foreach_array_item(p, name->providers)
+ repos |= p->pkg->repos;
+
+ /* repo 1 is always installed-db, so if other bits are set it means the package is available somewhere
+ * (either cache or in a proper repo)
+ */
+ return (repos & ~BIT(APK_REPOSITORY_CACHED)) == 0;
+}
+
+/* returns the currently installed package if there is a newer package that satisfies `name` */
+static const struct apk_package *is_upgradable(struct apk_name *name, const struct apk_package *pkg0)
+{
+ struct apk_provider *p;
+ struct apk_package *ipkg;
+ apk_blob_t *latest = apk_blob_atomize(APK_BLOB_STR(""));
+
+ if (name == NULL)
+ return NULL;
+
+ ipkg = apk_pkg_get_installed(name);
+ if (ipkg == NULL)
+ return NULL;
+
+ if (pkg0 == NULL)
+ {
+ foreach_array_item(p, name->providers)
+ {
+ pkg0 = p->pkg;
+ int r;
+
+ if (pkg0 == ipkg)
+ continue;
+
+ r = apk_version_compare_blob(*pkg0->version, *latest);
+ if (r == APK_VERSION_GREATER)
+ latest = pkg0->version;
+ }
+ }
+ else
+ latest = pkg0->version;
+
+ return apk_version_compare_blob(*ipkg->version, *latest) == APK_VERSION_LESS ? ipkg : NULL;
+}
+
+static void print_package(const struct apk_package *pkg, const struct list_ctx *ctx)
+{
+ printf(PKG_VER_FMT " " BLOB_FMT " ",
+ PKG_VER_PRINTF(pkg), BLOB_PRINTF(*pkg->arch));
+
+ if (pkg->origin != NULL)
+ printf("{" BLOB_FMT "}", BLOB_PRINTF(*pkg->origin));
+ else
+ printf("{%s}", pkg->name->name);
+
+ printf(" (" BLOB_FMT ")", BLOB_PRINTF(*pkg->license));
+
+ if (pkg->ipkg)
+ printf(" [installed]");
+ else
+ {
+ const struct apk_package *u;
+
+ u = is_upgradable(pkg->name, pkg);
+ if (u != NULL)
+ printf(" [upgradable from: " PKG_VER_FMT "]", PKG_VER_PRINTF(u));
+ }
+
+
+ if (apk_verbosity > 1)
+ {
+ printf("\n %s\n", pkg->description);
+ if (apk_verbosity > 2)
+ printf(" <%s>\n", pkg->url);
+ }
+
+ printf("\n");
+}
+
+static void filter_package(const struct apk_package *pkg, const struct list_ctx *ctx)
+{
+ if (ctx->match_origin && !origin_matches(ctx, pkg))
+ return;
+
+ if (ctx->installed && pkg->ipkg == NULL)
+ return;
+
+ if (ctx->orphaned && !is_orphaned(pkg->name))
+ return;
+
+ if (ctx->available && pkg->repos == BIT(APK_REPOSITORY_CACHED))
+ return;
+
+ if (ctx->upgradable && !is_upgradable(pkg->name, pkg))
+ return;
+
+ print_package(pkg, ctx);
+}
+
+static void iterate_providers(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;
+
+ if (ctx->match_providers)
+ printf("<%s> ", name->name);
+
+ filter_package(p->pkg, ctx);
+ }
+}
+
+static void print_result(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
+{
+ struct list_ctx *ctx = pctx;
+
+ if (name == NULL)
+ return;
+
+ if (ctx->match_depends)
+ {
+ struct apk_name **pname;
+
+ foreach_array_item(pname, name->rdepends)
+ iterate_providers(*pname, ctx);
+ }
+ else
+ iterate_providers(name, ctx);
+}
+
+static int option_parse_applet(void *pctx, struct apk_db_options *dbopts, int optch, const char *optarg)
+{
+ struct list_ctx *ctx = pctx;
+
+ switch (optch)
+ {
+ case 'I':
+ ctx->installed = 1;
+ break;
+ case 'O':
+ ctx->installed = 1;
+ ctx->orphaned = 1;
+ break;
+ case 'u':
+ ctx->available = 1;
+ ctx->orphaned = 0;
+ ctx->installed = 0;
+ ctx->upgradable = 1;
+ break;
+ case 'a':
+ ctx->available = 1;
+ ctx->orphaned = 0;
+ break;
+ case 'o':
+ ctx->match_origin = 1;
+ break;
+ case 'd':
+ ctx->match_depends = 1;
+ break;
+ case 'P':
+ ctx->match_providers = 1;
+ break;
+ default:
+ return -ENOTSUP;
+ }
+
+ return 0;
+}
+
+static const struct apk_option options_applet[] = {
+ { 'I', "installed" },
+ { 'O', "orphaned" },
+ { 'a', "available" },
+ { 'u', "upgradable" },
+ { 'o', "origin" },
+ { 'd', "depends" },
+ { 'P', "providers" },
+};
+
+static const struct apk_option_group optgroup_applet = {
+ .name = "List",
+ .options = options_applet,
+ .num_options = ARRAY_SIZE(options_applet),
+ .parse = option_parse_applet,
+};
+
+static int list_main(void *pctx, struct apk_database *db, struct apk_string_array *args)
+{
+ struct list_ctx *ctx = pctx;
+
+ ctx->filters = args;
+
+ if (ctx->match_origin)
+ args = NULL;
+
+ apk_name_foreach_matching(
+ db, args, APK_FOREACH_NULL_MATCHES_ALL | apk_foreach_genid(),
+ print_result, ctx);
+
+ return 0;
+}
+
+static struct apk_applet apk_list = {
+ .name = "list",
+ .arguments = "PATTERN",
+ .open_flags = APK_OPENF_READ,
+ .command_groups = APK_COMMAND_GROUP_QUERY,
+ .context_size = sizeof(struct list_ctx),
+ .optgroups = { &optgroup_global, &optgroup_applet },
+ .main = list_main,
+};
+
+APK_DEFINE_APPLET(apk_list);