commit:     735cc99067988a56c0e98ab1fdec55fc5d1014b1
Author:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Fri Feb  6 15:08:23 2026 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Fri Feb  6 15:08:23 2026 +0000
URL:        https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=735cc990

libq/tree: add tree_merge function

Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>

 libq/tree.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++----------
 libq/tree.h |  27 +++++++-----
 2 files changed, 130 insertions(+), 32 deletions(-)

diff --git a/libq/tree.c b/libq/tree.c
index e6f6f3d8..bb52fcfb 100644
--- a/libq/tree.c
+++ b/libq/tree.c
@@ -83,6 +83,7 @@ struct tree_ {
   char          *path;
   char          *repo;
   array         *cats;         /* list of tree_cat_ctx pointers */
+  array         *srctrees;     /* in case of TREE_MERGED */
   int            portroot_fd;
   enum {
     TREE_UNSET = 0,
@@ -91,6 +92,7 @@ struct tree_ {
     TREE_PACKAGES,
     TREE_BINPKGS,
     TREE_GTREE,
+    TREE_MERGED,
   }              type;
   bool           cats_complete:1;
 };
@@ -565,6 +567,57 @@ tree_ctx *tree_new
   return ret;
 }
 
+/* produces a new tree that is the merger of the trees tree1 and tree2
+ * NOTES:
+ * - the trees given should not be freed for as long as the merged tree
+ *   is around (not closed)
+ * - the individual trees (tree1, tree2) may be used outside of the
+ *   returned tree (except being freed)
+ * - no attempt is made to check for duplicates, the caller should merge
+ *   trees sensibly! */
+tree_ctx *tree_merge
+(
+  tree_ctx *tree1,
+  tree_ctx *tree2
+)
+{
+  tree_ctx *ret;
+
+  if (tree1 == NULL ||
+      tree2 == NULL)
+    return NULL;
+
+  if (tree1->type == TREE_MERGED)
+  {
+    ret   = tree1;
+    tree1 = NULL;
+  }
+  if (tree2->type == TREE_MERGED)
+  {
+    ret   = tree2;
+    tree2 = NULL;
+  }
+
+  if (tree1 == NULL &&
+      tree2 == NULL)
+    return NULL;  /* can't merge two merged trees for now */
+
+  if (ret == NULL)
+  {
+    ret = xzalloc(sizeof(*ret));
+    ret->type        = TREE_MERGED;
+    ret->srctrees    = array_new();
+    ret->portroot_fd = -1;
+  }
+
+  if (tree1 != NULL)
+    array_append(ret->srctrees, tree1);
+  if (tree2 != NULL)
+    array_append(ret->srctrees, tree2);
+
+  return ret;
+}
+
 /* helper to free up resources held by a package */
 static void tree_pkg_close
 (
@@ -1991,6 +2044,10 @@ int tree_foreach_pkg
   int           ret       = 0;
   bool          filtercat = false;
 
+  if (tree->type == TREE_MERGED)
+    err("programmer error: "
+        "cannot call tree_foreach_pkg on a tree of type MERGED");
+
   /* if we have a query with category, see if we have it already */
   if (query != NULL &&
       query->CATEGORY != NULL)
@@ -2410,7 +2467,7 @@ array *tree_match_atom
   /* a note on the flags that we control the output results with:
    * - LATEST:  only return the best (latest version) match for each PN
    * - FIRST:   stop searching after the first match (e.g. atom without
-   *            category), implies LATEST
+   *            category), implies LATEST on non-MERGED trees
    * - VIRTUAL: include the virtual category in results
    * - ACCT:    include the acct-user and acct-group categories in results
    * - SORT:    return the results in sorted order, this is implied by
@@ -2424,30 +2481,66 @@ array *tree_match_atom
       flags & TREE_MATCH_SORT   )
     sorted = true;
 
-  tree_foreach_pkg(tree, tree_match_atom_cb, ret, sorted, atom);
-
-  if (!(flags & TREE_MATCH_VIRTUAL))
+  /* handle merged tree separately */
+  if (tree->type == TREE_MERGED)
   {
-    array_for_each_rev(ret, n, w)
-      if (strcmp(tree_pkg_get_cat_name(w), "virtual") == 0)
-        array_remove(ret, n);
-  }
+    tree_ctx *stree;
+    array    *match;
+
+    /* behaviour of flags is in line here, but because the context is
+     * slightly different:
+     * - LATEST: find the highest version in all of the trees
+     * - FIRST:  return the pkg from the first tree with a match
+     * this allows to respect the order (e.g. VDB -> BINPKG -> TREE) of
+     * preference without any extra checks from the caller */
+    array_for_each(tree->srctrees, n, stree)
+    {
+      match = tree_match_atom(stree, atom, flags);
+      if (array_cnt(match) > 0)
+      {
+        array_move(ret, match);
+        array_free(match);
+        if (flags & TREE_MATCH_FIRST)
+          break;
+      }
+      else
+      {
+        array_free(match);
+      }
+    }
 
-  if (!(flags & TREE_MATCH_ACCT))
-  {
-    array_for_each_rev(ret, n, w)
-      if (strncmp("acct-", tree_pkg_get_cat_name(w), sizeof("acct-") - 1) == 0)
-        array_remove(ret, n);
+    /* need to re-sort because we pushed results from multiple trees */
+    if (sorted)
+      array_sort(ret, tree_pkg_compar);
   }
-
-  if (flags & TREE_MATCH_FIRST &&
-      array_cnt(ret) > 1)
+  else
   {
-    /* a bit crude, we can optimise this later */
-    array *new = array_new();
-    array_append(new, array_get(ret, 0));
-    array_free(ret);
-    ret = new;
+    tree_foreach_pkg(tree, tree_match_atom_cb, ret, sorted, atom);
+
+    if (!(flags & TREE_MATCH_VIRTUAL))
+    {
+      array_for_each_rev(ret, n, w)
+        if (strcmp(tree_pkg_get_cat_name(w), "virtual") == 0)
+          array_remove(ret, n);
+    }
+
+    if (!(flags & TREE_MATCH_ACCT))
+    {
+      array_for_each_rev(ret, n, w)
+        if (strncmp("acct-",
+                    tree_pkg_get_cat_name(w), sizeof("acct-") - 1) == 0)
+          array_remove(ret, n);
+    }
+
+    if (flags & TREE_MATCH_FIRST &&
+        array_cnt(ret) > 1)
+    {
+      /* a bit crude, we can optimise this later */
+      array *new = array_new();
+      array_append(new, array_get(ret, 0));
+      array_free(ret);
+      ret = new;
+    }
   }
 
   if (flags & TREE_MATCH_LATEST)

diff --git a/libq/tree.h b/libq/tree.h
index 82868bf6..838a8756 100644
--- a/libq/tree.h
+++ b/libq/tree.h
@@ -78,30 +78,35 @@ enum tree_pkg_meta_keys {
   TREE_META_MAX_KEYS
 };
 
-tree_ctx *tree_new(const char *portroot, const char *path, enum tree_open_type 
type, bool quiet);
-void tree_close(tree_ctx *tree);
-
-int tree_foreach_pkg(tree_ctx *tree, tree_pkg_cb callback, void *priv, bool 
sorted, const atom_ctx *query);
-
-array *tree_match_atom(tree_ctx *tree, const atom_ctx *query, int flags);
+tree_ctx           *tree_new(const char *portroot, const char *path,
+                             enum tree_open_type type, bool quiet);
+tree_ctx           *tree_merge(tree_ctx *tree1, tree_ctx *tree2);
+void                tree_close(tree_ctx *tree);
+
+int                 tree_foreach_pkg(tree_ctx *tree, tree_pkg_cb callback,
+                                     void *priv, bool sorted,
+                                     const atom_ctx *query);
+array              *tree_match_atom(tree_ctx *tree, const atom_ctx *query,
+                                    int flags);
 #define TREE_MATCH_LATEST     (1<<3)
 #define TREE_MATCH_FIRST      (1<<4)
 #define TREE_MATCH_VIRTUAL    (1<<5)
 #define TREE_MATCH_ACCT       (1<<6)
 #define TREE_MATCH_SORT       (1<<7)
 #define TREE_MATCH_DEFAULT    (TREE_MATCH_VIRTUAL | \
-                               TREE_MATCH_ACCT | \
-                               TREE_MATCH_SORT)
+                               TREE_MATCH_ACCT    | \
+                               TREE_MATCH_SORT    )
 
-tree_metadata_xml *tree_pkg_metadata(tree_pkg_ctx *pkg_ctx);
-void               tree_close_metadata(tree_metadata_xml *meta_ctx);
+tree_metadata_xml  *tree_pkg_metadata(tree_pkg_ctx *pkg_ctx);
+void                tree_close_metadata(tree_metadata_xml *meta_ctx);
 
 char               *tree_get_repo_name(tree_ctx *tree);
 char               *tree_get_path(tree_ctx *tree);
 int                 tree_get_portroot_fd(tree_ctx *tree);
 enum tree_open_type tree_get_treetype(tree_ctx *tree);
 
-char               *tree_pkg_meta(tree_pkg_ctx *pkg, enum tree_pkg_meta_keys 
key);
+char               *tree_pkg_meta(tree_pkg_ctx *pkg,
+                                  enum tree_pkg_meta_keys key);
 atom_ctx           *tree_pkg_atom(tree_pkg_ctx *pkg, bool full);
 tree_ctx           *tree_pkg_get_tree(tree_pkg_ctx *pkg);
 char               *tree_pkg_get_cat_name(tree_pkg_ctx *pkg);

Reply via email to