176 lines
3.6 KiB
C
176 lines
3.6 KiB
C
/* app_dot.c - Alpine Package Keeper (APK)
|
|
*
|
|
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
|
|
* All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <fnmatch.h>
|
|
|
|
#include "apk_applet.h"
|
|
#include "apk_database.h"
|
|
#include "apk_print.h"
|
|
|
|
#define S_EVALUATED -1
|
|
#define S_EVALUATING -2
|
|
|
|
struct dot_ctx {
|
|
int not_empty : 1;
|
|
int errors_only : 1;
|
|
int installed_only : 1;
|
|
};
|
|
|
|
#define DOT_OPTIONS(OPT) \
|
|
OPT(OPT_DOT_errors, "errors") \
|
|
OPT(OPT_DOT_installed, "installed")
|
|
|
|
APK_OPT_APPLET(option_desc, DOT_OPTIONS);
|
|
|
|
static int option_parse_applet(void *pctx, struct apk_ctx *ac, int opt, const char *optarg)
|
|
{
|
|
struct dot_ctx *ctx = (struct dot_ctx *) pctx;
|
|
|
|
switch (opt) {
|
|
case OPT_DOT_errors:
|
|
ctx->errors_only = 1;
|
|
break;
|
|
case OPT_DOT_installed:
|
|
ctx->installed_only = 1;
|
|
ac->open_flags &= ~APK_OPENF_NO_INSTALLED;
|
|
break;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct apk_option_group optgroup_applet = {
|
|
.desc = option_desc,
|
|
.parse = option_parse_applet,
|
|
};
|
|
|
|
static void start_graph(struct dot_ctx *ctx)
|
|
{
|
|
if (ctx->not_empty)
|
|
return;
|
|
ctx->not_empty = 1;
|
|
|
|
printf( "digraph \"apkindex\" {\n"
|
|
" rankdir=LR;\n"
|
|
" node [shape=box];\n");
|
|
}
|
|
|
|
static void dump_name(struct dot_ctx *ctx, struct apk_name *name)
|
|
{
|
|
if (name->state_int)
|
|
return;
|
|
name->state_int = 1;
|
|
|
|
if (name->providers->num == 0) {
|
|
start_graph(ctx);
|
|
printf(" \"%s\" [style=dashed, color=red, fontcolor=red, shape=octagon];\n",
|
|
name->name);
|
|
}
|
|
}
|
|
|
|
static int dump_pkg(struct dot_ctx *ctx, struct apk_package *pkg)
|
|
{
|
|
struct apk_dependency *dep;
|
|
struct apk_provider *p0;
|
|
int r, ret = 0;
|
|
|
|
if (ctx->installed_only && pkg->ipkg == NULL)
|
|
return 0;
|
|
|
|
if (pkg->state_int == S_EVALUATED)
|
|
return 0;
|
|
|
|
if (pkg->state_int <= S_EVALUATING) {
|
|
pkg->state_int--;
|
|
return 1;
|
|
}
|
|
|
|
pkg->state_int = S_EVALUATING;
|
|
foreach_array_item(dep, pkg->depends) {
|
|
struct apk_name *name = dep->name;
|
|
|
|
dump_name(ctx, name);
|
|
|
|
if (name->providers->num == 0) {
|
|
printf(" \"" PKG_VER_FMT "\" -> \"%s\" [color=red];\n",
|
|
PKG_VER_PRINTF(pkg), name->name);
|
|
continue;
|
|
}
|
|
|
|
foreach_array_item(p0, name->providers) {
|
|
if (ctx->installed_only && p0->pkg->ipkg == NULL)
|
|
continue;
|
|
if (!apk_dep_is_provided(dep, p0))
|
|
continue;
|
|
|
|
r = dump_pkg(ctx, p0->pkg);
|
|
ret += r;
|
|
if (r || (!ctx->errors_only)) {
|
|
start_graph(ctx);
|
|
|
|
printf(" \"" PKG_VER_FMT "\" -> \"" PKG_VER_FMT "\"[",
|
|
PKG_VER_PRINTF(pkg),
|
|
PKG_VER_PRINTF(p0->pkg));
|
|
if (r)
|
|
printf("color=red,");
|
|
if (p0->pkg->name != dep->name)
|
|
printf("arrowhead=inv,label=\"%s\",", dep->name->name);
|
|
printf("];\n");
|
|
}
|
|
}
|
|
}
|
|
ret -= S_EVALUATING - pkg->state_int;
|
|
pkg->state_int = S_EVALUATED;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int foreach_pkg(apk_hash_item item, void *ctx)
|
|
{
|
|
dump_pkg((struct dot_ctx *) ctx, (struct apk_package *) item);
|
|
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_provider *p;
|
|
char **parg;
|
|
|
|
if (args->num) {
|
|
foreach_array_item(parg, args) {
|
|
struct apk_name *name = apk_db_get_name(db, APK_BLOB_STR(*parg));
|
|
if (!name)
|
|
continue;
|
|
foreach_array_item(p, name->providers)
|
|
dump_pkg(ctx, p->pkg);
|
|
}
|
|
} else {
|
|
apk_hash_foreach(&db->available.packages, foreach_pkg, pctx);
|
|
}
|
|
|
|
if (!ctx->not_empty)
|
|
return 1;
|
|
|
|
printf("}\n");
|
|
return 0;
|
|
}
|
|
|
|
static struct apk_applet apk_dot = {
|
|
.name = "dot",
|
|
.open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE,
|
|
.context_size = sizeof(struct dot_ctx),
|
|
.optgroups = { &optgroup_global, &optgroup_applet },
|
|
.main = dot_main,
|
|
};
|
|
|
|
APK_DEFINE_APPLET(apk_dot);
|