2020-02-14 11:49:41 +00:00
|
|
|
/* app_list.c - Alpine Package Keeper (APK)
|
2018-01-28 23:55:29 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2020-04-22 13:33:41 +00:00
|
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
2018-01-28 23:55:29 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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 {
|
2020-10-05 15:52:51 +00:00
|
|
|
int verbosity;
|
2018-01-28 23:55:29 +00:00
|
|
|
unsigned int installed : 1;
|
|
|
|
unsigned int orphaned : 1;
|
|
|
|
unsigned int available : 1;
|
|
|
|
unsigned int upgradable : 1;
|
|
|
|
unsigned int match_origin : 1;
|
2018-01-29 02:55:29 +00:00
|
|
|
unsigned int match_depends : 1;
|
2018-01-29 03:17:07 +00:00
|
|
|
unsigned int match_providers : 1;
|
2021-12-23 13:43:11 +00:00
|
|
|
unsigned int manifest : 1;
|
2018-01-28 23:55:29 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2020-10-05 15:52:51 +00:00
|
|
|
foreach_array_item(pmatch, ctx->filters) {
|
2018-01-28 23:55:29 +00:00
|
|
|
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)
|
|
|
|
*/
|
2019-05-01 13:59:16 +00:00
|
|
|
return (repos & ~BIT(APK_REPOSITORY_CACHED)) == 0;
|
2018-01-28 23:55:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
2020-05-19 08:39:21 +00:00
|
|
|
apk_blob_t no_version = APK_BLOB_STR("");
|
|
|
|
apk_blob_t *latest = &no_version;
|
|
|
|
int r;
|
2018-01-28 23:55:29 +00:00
|
|
|
|
2020-05-19 08:39:21 +00:00
|
|
|
if (!name) return NULL;
|
2018-01-28 23:55:29 +00:00
|
|
|
|
|
|
|
ipkg = apk_pkg_get_installed(name);
|
2020-05-19 08:39:21 +00:00
|
|
|
if (!ipkg) return NULL;
|
2018-01-28 23:55:29 +00:00
|
|
|
|
2020-05-19 08:39:21 +00:00
|
|
|
if (!pkg0) {
|
|
|
|
foreach_array_item(p, name->providers) {
|
2018-01-28 23:55:29 +00:00
|
|
|
pkg0 = p->pkg;
|
2020-05-19 08:39:21 +00:00
|
|
|
if (pkg0 == ipkg) continue;
|
2018-01-28 23:55:29 +00:00
|
|
|
r = apk_version_compare_blob(*pkg0->version, *latest);
|
2020-05-19 08:39:21 +00:00
|
|
|
if (r == APK_VERSION_GREATER) latest = pkg0->version;
|
2018-01-28 23:55:29 +00:00
|
|
|
}
|
2020-05-19 08:39:21 +00:00
|
|
|
} else {
|
2018-01-28 23:55:29 +00:00
|
|
|
latest = pkg0->version;
|
2020-05-19 08:39:21 +00:00
|
|
|
}
|
2018-01-28 23:55:29 +00:00
|
|
|
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)
|
|
|
|
{
|
2018-06-29 13:39:56 +00:00
|
|
|
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));
|
2018-01-28 23:55:29 +00:00
|
|
|
|
|
|
|
if (pkg->ipkg)
|
|
|
|
printf(" [installed]");
|
2020-10-05 15:52:51 +00:00
|
|
|
else {
|
2018-01-28 23:55:29 +00:00
|
|
|
const struct apk_package *u;
|
|
|
|
|
|
|
|
u = is_upgradable(pkg->name, pkg);
|
|
|
|
if (u != NULL)
|
|
|
|
printf(" [upgradable from: " PKG_VER_FMT "]", PKG_VER_PRINTF(u));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-05 15:52:51 +00:00
|
|
|
if (ctx->verbosity > 1) {
|
2018-01-28 23:55:29 +00:00
|
|
|
printf("\n %s\n", pkg->description);
|
2020-10-05 15:52:51 +00:00
|
|
|
if (ctx->verbosity > 2)
|
2018-01-28 23:55:29 +00:00
|
|
|
printf(" <%s>\n", pkg->url);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
2021-12-23 13:43:11 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2018-01-29 02:39:39 +00:00
|
|
|
static void filter_package(const struct apk_package *pkg, const struct list_ctx *ctx)
|
2018-01-28 23:55:29 +00:00
|
|
|
{
|
2018-01-29 02:39:39 +00:00
|
|
|
if (ctx->match_origin && !origin_matches(ctx, pkg))
|
|
|
|
return;
|
2018-01-28 23:55:29 +00:00
|
|
|
|
2018-01-29 02:39:39 +00:00
|
|
|
if (ctx->installed && pkg->ipkg == NULL)
|
2018-01-28 23:55:29 +00:00
|
|
|
return;
|
|
|
|
|
2018-01-29 02:39:39 +00:00
|
|
|
if (ctx->orphaned && !is_orphaned(pkg->name))
|
|
|
|
return;
|
2018-01-28 23:55:29 +00:00
|
|
|
|
2019-05-01 13:59:16 +00:00
|
|
|
if (ctx->available && pkg->repos == BIT(APK_REPOSITORY_CACHED))
|
2018-01-29 02:39:39 +00:00
|
|
|
return;
|
2018-01-28 23:55:29 +00:00
|
|
|
|
2018-01-29 02:39:39 +00:00
|
|
|
if (ctx->upgradable && !is_upgradable(pkg->name, pkg))
|
|
|
|
return;
|
2018-01-28 23:55:29 +00:00
|
|
|
|
2021-12-23 13:43:11 +00:00
|
|
|
if (ctx->manifest)
|
|
|
|
print_manifest(pkg, ctx);
|
|
|
|
else
|
|
|
|
print_package(pkg, ctx);
|
2018-01-29 02:39:39 +00:00
|
|
|
}
|
2018-01-28 23:55:29 +00:00
|
|
|
|
2018-01-29 02:55:29 +00:00
|
|
|
static void iterate_providers(const struct apk_name *name, const struct list_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct apk_provider *p;
|
|
|
|
|
2020-10-05 15:52:51 +00:00
|
|
|
foreach_array_item(p, name->providers) {
|
2018-01-29 03:17:07 +00:00
|
|
|
if (!ctx->match_providers && p->pkg->name != name)
|
2018-01-29 02:55:29 +00:00
|
|
|
continue;
|
|
|
|
|
2018-01-29 03:17:07 +00:00
|
|
|
if (ctx->match_providers)
|
|
|
|
printf("<%s> ", name->name);
|
|
|
|
|
2018-01-29 02:55:29 +00:00
|
|
|
filter_package(p->pkg, ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-29 02:39:39 +00:00
|
|
|
static void print_result(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
|
|
|
|
{
|
|
|
|
struct list_ctx *ctx = pctx;
|
2020-10-05 15:52:51 +00:00
|
|
|
struct apk_name **pname;
|
2018-01-28 23:55:29 +00:00
|
|
|
|
2018-01-29 02:39:39 +00:00
|
|
|
if (name == NULL)
|
|
|
|
return;
|
2018-01-28 23:55:29 +00:00
|
|
|
|
2020-10-05 15:52:51 +00:00
|
|
|
if (ctx->match_depends) {
|
2018-01-29 02:55:29 +00:00
|
|
|
foreach_array_item(pname, name->rdepends)
|
|
|
|
iterate_providers(*pname, ctx);
|
2020-10-05 15:52:51 +00:00
|
|
|
} else {
|
2018-01-29 02:55:29 +00:00
|
|
|
iterate_providers(name, ctx);
|
2020-10-05 15:52:51 +00:00
|
|
|
}
|
2018-01-28 23:55:29 +00:00
|
|
|
}
|
|
|
|
|
2020-08-24 10:35:36 +00:00
|
|
|
#define LIST_OPTIONS(OPT) \
|
|
|
|
OPT(OPT_LIST_available, APK_OPT_SH("a") "available") \
|
|
|
|
OPT(OPT_LIST_installed, APK_OPT_SH("I") "installed") \
|
|
|
|
OPT(OPT_LIST_depends, APK_OPT_SH("d") "depends") \
|
|
|
|
OPT(OPT_LIST_origin, APK_OPT_SH("o") "origin") \
|
|
|
|
OPT(OPT_LIST_orphaned, APK_OPT_SH("O") "orphaned") \
|
|
|
|
OPT(OPT_LIST_providers, APK_OPT_SH("P") "providers") \
|
2021-08-23 08:07:50 +00:00
|
|
|
OPT(OPT_LIST_upgradable, APK_OPT_SH("u") "upgradable") \
|
2021-12-23 13:43:11 +00:00
|
|
|
OPT(OPT_LIST_manifest, "manifest") \
|
2021-08-23 08:07:50 +00:00
|
|
|
OPT(OPT_LIST_upgradeable, "upgradeable")
|
2020-08-24 10:35:36 +00:00
|
|
|
|
|
|
|
APK_OPT_APPLET(option_desc, LIST_OPTIONS);
|
2020-05-04 18:45:11 +00:00
|
|
|
|
2020-10-05 15:52:51 +00:00
|
|
|
static int option_parse_applet(void *pctx, struct apk_ctx *ac, int opt, const char *optarg)
|
2018-01-28 23:55:29 +00:00
|
|
|
{
|
|
|
|
struct list_ctx *ctx = pctx;
|
|
|
|
|
2020-05-04 18:45:11 +00:00
|
|
|
switch (opt) {
|
|
|
|
case OPT_LIST_available:
|
|
|
|
ctx->available = 1;
|
|
|
|
ctx->orphaned = 0;
|
|
|
|
break;
|
|
|
|
case OPT_LIST_installed:
|
2018-01-28 23:55:29 +00:00
|
|
|
ctx->installed = 1;
|
|
|
|
break;
|
2020-05-04 18:45:11 +00:00
|
|
|
case OPT_LIST_depends:
|
|
|
|
ctx->match_depends = 1;
|
|
|
|
break;
|
|
|
|
case OPT_LIST_origin:
|
|
|
|
ctx->match_origin = 1;
|
|
|
|
break;
|
|
|
|
case OPT_LIST_orphaned:
|
2018-01-28 23:55:29 +00:00
|
|
|
ctx->installed = 1;
|
|
|
|
ctx->orphaned = 1;
|
|
|
|
break;
|
2020-05-04 18:45:11 +00:00
|
|
|
case OPT_LIST_providers:
|
|
|
|
ctx->match_providers = 1;
|
|
|
|
break;
|
2021-12-23 13:43:11 +00:00
|
|
|
case OPT_LIST_manifest:
|
|
|
|
ctx->manifest = 1;
|
|
|
|
ctx->installed = 1;
|
|
|
|
break;
|
2021-08-23 08:07:50 +00:00
|
|
|
case OPT_LIST_upgradable:
|
2020-05-04 18:45:11 +00:00
|
|
|
case OPT_LIST_upgradeable:
|
2018-01-28 23:55:29 +00:00
|
|
|
ctx->available = 1;
|
|
|
|
ctx->orphaned = 0;
|
|
|
|
ctx->installed = 0;
|
|
|
|
ctx->upgradable = 1;
|
|
|
|
break;
|
|
|
|
default:
|
2019-06-05 06:32:29 +00:00
|
|
|
return -ENOTSUP;
|
2018-01-28 23:55:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct apk_option_group optgroup_applet = {
|
2020-05-04 18:45:11 +00:00
|
|
|
.desc = option_desc,
|
2018-01-28 23:55:29 +00:00
|
|
|
.parse = option_parse_applet,
|
|
|
|
};
|
|
|
|
|
2020-10-09 09:44:32 +00:00
|
|
|
static int list_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
|
2018-01-28 23:55:29 +00:00
|
|
|
{
|
2020-10-09 09:44:32 +00:00
|
|
|
struct apk_out *out = &ac->out;
|
|
|
|
struct apk_database *db = ac->db;
|
2018-01-28 23:55:29 +00:00
|
|
|
struct list_ctx *ctx = pctx;
|
|
|
|
|
2020-10-09 09:44:32 +00:00
|
|
|
ctx->verbosity = apk_out_verbosity(out);
|
2018-01-28 23:55:29 +00:00
|
|
|
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",
|
|
|
|
.open_flags = APK_OPENF_READ,
|
|
|
|
.context_size = sizeof(struct list_ctx),
|
|
|
|
.optgroups = { &optgroup_global, &optgroup_applet },
|
|
|
|
.main = list_main,
|
|
|
|
};
|
|
|
|
|
|
|
|
APK_DEFINE_APPLET(apk_list);
|