diff --git a/libpkgconf/libpkgconf.h b/libpkgconf/libpkgconf.h index e36cf96..7a7e463 100644 --- a/libpkgconf/libpkgconf.h +++ b/libpkgconf/libpkgconf.h @@ -137,6 +137,8 @@ struct pkgconf_path_ { #define PKGCONF_PKG_PROPF_CACHED 0x02 #define PKGCONF_PKG_PROPF_UNINSTALLED 0x08 #define PKGCONF_PKG_PROPF_VIRTUAL 0x10 +#define PKGCONF_PKG_PROPF_VISITED 0x20 +#define PKGCONF_PKG_PROPF_VISITED_PRIVATE 0x40 struct pkgconf_pkg_ { int refcount; @@ -176,6 +178,7 @@ struct pkgconf_pkg_ { uint64_t serial; uint64_t identifier; + uint64_t traverse_serial; }; typedef bool (*pkgconf_pkg_iteration_func_t)(const pkgconf_pkg_t *pkg, void *data); @@ -212,6 +215,7 @@ struct pkgconf_client_ { uint64_t serial; uint64_t identifier; + uint64_t traverse_serial; pkgconf_pkg_t **cache_table; size_t cache_count; diff --git a/libpkgconf/pkg.c b/libpkgconf/pkg.c index fc73bf3..a8df57c 100644 --- a/libpkgconf/pkg.c +++ b/libpkgconf/pkg.c @@ -1624,10 +1624,28 @@ pkgconf_pkg_traverse_main(pkgconf_client_t *client, unsigned int skip_flags) { unsigned int eflags = PKGCONF_PKG_ERRF_OK; + unsigned int visited_flag = (client->flags & PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE) ? PKGCONF_PKG_PROPF_VISITED_PRIVATE : PKGCONF_PKG_PROPF_VISITED; if (maxdepth == 0) return eflags; + /* If we have already visited this node, check if we have done so as a Requires or Requires.private + * query as appropriate, and short-circuit if so. + */ + if ((root->flags & PKGCONF_PKG_PROPF_VIRTUAL) == 0 && root->traverse_serial == client->traverse_serial) + { + if (root->flags & visited_flag) + return eflags; + } + else + { + root->traverse_serial = client->traverse_serial; + root->flags &= ~(PKGCONF_PKG_PROPF_VISITED|PKGCONF_PKG_PROPF_VISITED_PRIVATE); + } + + /* Update this node to indicate how we've visited it so far. */ + root->flags |= visited_flag; + PKGCONF_TRACE(client, "%s: level %d, serial %"PRIu64, root->id, maxdepth, client->serial); if ((root->flags & PKGCONF_PKG_PROPF_VIRTUAL) != PKGCONF_PKG_PROPF_VIRTUAL || (client->flags & PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL) != PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL) @@ -1675,6 +1693,8 @@ pkgconf_pkg_traverse(pkgconf_client_t *client, if (root->flags & PKGCONF_PKG_PROPF_VIRTUAL) client->serial++; + client->traverse_serial++; + return pkgconf_pkg_traverse_main(client, root, func, data, maxdepth, skip_flags); }