list: new applet
The list applet provides a convenient way of inspecting both the available and installed package databases by listing their contents. In some ways, it is similar to `apk search` but is considered to be a superset of `apk search` functionality. A few `apk list` criterion are not yet ready though, such as `apk list --depends` which searches by runtime dependency (replacing `apk info --rdepends`).cute-signatures
parent
5da4dec2a2
commit
fff8bfa588
|
@ -23,7 +23,7 @@ install-LUA_LIB-y := $(INSTALLDIR) $(DESTDIR)$(LUA_LIBDIR) && \
|
|||
endif
|
||||
|
||||
progs-y += apk
|
||||
apk-objs := apk.o add.o del.o fix.o update.o info.o \
|
||||
apk-objs := apk.o add.o del.o fix.o update.o info.o list.o \
|
||||
search.o upgrade.o cache.o ver.o index.o fetch.o \
|
||||
audit.o verify.o dot.o policy.o stats.o manifest.o
|
||||
|
||||
|
|
|
@ -0,0 +1,235 @@
|
|||
/* 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;
|
||||
|
||||
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(1)) == 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 " {" BLOB_FMT "} (" BLOB_FMT ")",
|
||||
PKG_VER_PRINTF(pkg), BLOB_PRINTF(*pkg->arch), BLOB_PRINTF(*pkg->origin),
|
||||
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 print_result(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
|
||||
{
|
||||
struct list_ctx *ctx = pctx;
|
||||
struct apk_provider *p;
|
||||
struct apk_package *pkg;
|
||||
|
||||
if (name == NULL)
|
||||
return;
|
||||
|
||||
foreach_array_item(p, name->providers)
|
||||
{
|
||||
pkg = p->pkg;
|
||||
|
||||
if (ctx->match_origin && !origin_matches(ctx, pkg))
|
||||
continue;
|
||||
|
||||
if (ctx->installed && pkg->ipkg == NULL)
|
||||
continue;
|
||||
|
||||
if (ctx->orphaned && !is_orphaned(name))
|
||||
continue;
|
||||
|
||||
if (ctx->available && pkg->repos == BIT(1))
|
||||
continue;
|
||||
|
||||
if (ctx->upgradable && !is_upgradable(name, pkg))
|
||||
continue;
|
||||
|
||||
print_package(pkg, 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;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct apk_option options_applet[] = {
|
||||
{ 'I', "installed", "List installed packages only" },
|
||||
{ 'O', "orphaned", "List orphaned packages only" },
|
||||
{ 'a', "available", "List available packages only" },
|
||||
{ 'u', "upgradable", "List upgradable packages only" },
|
||||
{ 'o', "origin", "List packages by origin" },
|
||||
};
|
||||
|
||||
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",
|
||||
.help = "List packages by PATTERN and other criteria",
|
||||
.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);
|
Loading…
Reference in New Issue