From 321bb2ac1bd4a88addfd2875f467d2e24b8a0005 Mon Sep 17 00:00:00 2001
From: Michael Schroeder <mls@suse.de>
Date: Fri, 5 Oct 2018 14:41:20 +0200
Subject: [PATCH] Add selection_make_matchsolvable and
 selection_make_matchsolvablelist

This is like pool_whatmatchessolvable, but works on a selection.
The advantage is that it supports filtering.
---
 examples/solv/solv.c |  36 ++---------
 src/libsolv.ver      |   2 +
 src/selection.c      | 149 ++++++++++++++++++++++++++++++++++++++++++-
 src/selection.h      |   6 +-
 4 files changed, 162 insertions(+), 31 deletions(-)

diff --git a/src/libsolv.ver b/src/libsolv.ver
index a3fa19a..9f6d324 100644
--- a/src/libsolv.ver
+++ b/src/libsolv.ver
@@ -249,6 +249,8 @@ SOLV_1.0 {
 		selection_make;
 		selection_make_matchdepid;
 		selection_make_matchdeps;
+		selection_make_matchsolvable;
+		selection_make_matchsolvablelist;
 		selection_solvables;
 		solv_bin2hex;
 		solv_calloc;
diff --git a/src/selection.c b/src/selection.c
index d44c482..22ad3b0 100644
--- a/src/selection.c
+++ b/src/selection.c
@@ -1430,6 +1430,137 @@ matchdep(Pool *pool, Id id, char *rname, int rflags, Id revr, int flags)
   return matchdep_str(rname, pool_id2str(pool, id), flags);
 }
 
+static int
+selection_make_matchsolvable_common_limited(Pool *pool, Queue *selection, Queue *solvidq, Id solvid, int flags, int keyname, int marker, struct limiter *limiter)
+{
+  Id *wp;
+  Map m, missc;
+  int reloff, boff;
+  int li, i, j;
+  Id p;
+  Queue q;
+
+  if (solvidq)
+    {
+      map_init(&m, pool->nsolvables);
+      for (i = 0; i < solvidq->count; i++)
+	MAPSET(&m, solvidq->elements[i]);
+    }
+  queue_init(&q);
+  reloff = pool->ss.nstrings;
+  map_init(&missc, reloff + pool->nrels);
+  for (li = limiter->start; li < limiter->end; li++)
+    {
+      Solvable *s;
+      p = limiter->mapper ? limiter->mapper[li] : li;
+      if (solvidq && MAPTST(&m, p))
+	continue;
+      if (!solvidq && p == solvid)
+	continue;
+      s = pool->solvables + p;
+      if (!s->repo || (limiter->repofilter && s->repo != limiter->repofilter))
+	continue;
+      if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+	{
+	  if (!(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE))
+	    continue;
+	  if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s))
+	    continue;
+	}
+      else
+	{
+	  if ((flags & SELECTION_SOURCE_ONLY) != 0)
+	    continue;
+	  if (s->repo != pool->installed)
+	    {
+	      if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s))
+		continue;
+	      if (!(flags & SELECTION_WITH_BADARCH) && pool_badarch_solvable(pool, s))
+		continue;
+	    }
+	}
+      if (q.count)
+	queue_empty(&q);
+      repo_lookup_deparray(s->repo, p, keyname, &q, marker);
+      if (!q.count)
+	continue;
+      for (i = 0; i < q.count; i++)
+	{
+	  Id dep = q.elements[i];
+	  boff = ISRELDEP(dep) ? reloff + GETRELID(dep) : dep;
+	  if (MAPTST(&missc, boff))
+	    continue;
+	  if (ISRELDEP(dep))
+	    {
+	      Reldep *rd = GETRELDEP(pool, dep);
+	      if (!ISRELDEP(rd->name) && rd->flags < 8)
+		{
+		  /* do pre-filtering on the base */
+		  if (MAPTST(&missc, rd->name))
+		    continue;
+		  wp = pool_whatprovides_ptr(pool, rd->name);
+		  if (solvidq)
+		    {
+		      for (wp = pool_whatprovides_ptr(pool, dep); *wp; wp++)
+			if (MAPTST(&m, *wp))
+			  break;
+		    }
+		  else
+		    {
+		      for (wp = pool_whatprovides_ptr(pool, dep); *wp; wp++)
+			if (*wp == solvid)
+			  break;
+		    }
+		  if (!*wp)
+		    {
+		      /* the base does not include solvid, no need to check the complete dep */
+		      MAPSET(&missc, rd->name);
+		      MAPSET(&missc, boff);
+		      continue;
+		    }
+		}
+	    }
+	  wp = pool_whatprovides_ptr(pool, dep);
+	  if (solvidq)
+	    {
+	      for (wp = pool_whatprovides_ptr(pool, dep); *wp; wp++)
+		if (MAPTST(&m, *wp))
+		  break;
+	    }
+	  else
+	    {
+	      for (wp = pool_whatprovides_ptr(pool, dep); *wp; wp++)
+		if (*wp == solvid)
+		  break;
+	    }
+	  if (*wp)
+	    {
+	      queue_push(selection, p);
+	      break;
+	    }
+	  MAPSET(&missc, boff);
+	}
+    }
+  queue_free(&q);
+  map_free(&missc);
+  if (solvidq)
+    map_free(&m);
+  if (!selection->count)
+    return 0;
+
+  /* convert package list to selection */
+  j = selection->count;
+  queue_insertn(selection, 0, selection->count, 0);
+  for (i = 0; i < selection->count; )
+    {
+      selection->elements[i++] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
+      selection->elements[i++] = selection->elements[j++];
+    }
+  if ((flags & SELECTION_FLAT) != 0)
+    selection_flatten(pool, selection);
+  return SELECTION_PROVIDES;
+}
+
 static int
 selection_make_matchdeps_common_limited(Pool *pool, Queue *selection, const char *name, Id dep, int flags, int keyname, int marker, struct limiter *limiter)
 {
@@ -1449,6 +1580,9 @@ selection_make_matchdeps_common_limited(Pool *pool, Queue *selection, const char
   if (name && dep)
     return 0;
 
+  if ((flags & SELECTION_MATCH_SOLVABLE) != 0)
+    return selection_make_matchsolvable_common_limited(pool, selection, (Queue *)name, dep, flags, keyname, marker, limiter);
+
   if ((flags & SELECTION_MATCH_DEPSTR) != 0)
     flags &= ~SELECTION_REL;
 
@@ -1558,7 +1692,8 @@ selection_make_matchdeps_common_limited(Pool *pool, Queue *selection, const char
 	  queue_push(selection, p);
 	  continue;
 	}
-      queue_empty(&q);
+      if (q.count)
+        queue_empty(&q);
       repo_lookup_deparray(s->repo, p, keyname, &q, marker);
       if (!q.count)
 	continue;
@@ -1708,6 +1843,18 @@ selection_make_matchdepid(Pool *pool, Queue *selection, Id dep, int flags, int k
   return selection_make_matchdeps_common(pool, selection, 0, dep, flags, keyname, marker);
 }
 
+int
+selection_make_matchsolvable(Pool *pool, Queue *selection, Id solvid, int flags, int keyname, int marker)
+{
+  return selection_make_matchdeps_common(pool, selection, 0, solvid, flags | SELECTION_MATCH_SOLVABLE, keyname, marker);
+}
+
+int
+selection_make_matchsolvablelist(Pool *pool, Queue *selection, Queue *solvidq, int flags, int keyname, int marker)
+{
+  return selection_make_matchdeps_common(pool, selection, (const char *)solvidq, 0, flags | SELECTION_MATCH_SOLVABLE, keyname, marker);
+}
+
 static inline int
 pool_is_kind(Pool *pool, Id name, Id kind)
 {
diff --git a/src/selection.h b/src/selection.h
index 9938c2f..f3ed2c2 100644
--- a/src/selection.h
+++ b/src/selection.h
@@ -52,16 +52,20 @@ extern "C" {
 #define SELECTION_SUBTRACT		(2 << 28)
 #define SELECTION_FILTER		(3 << 28)
 
-#define SELECTION_MODEBITS		(3 << 28)	/* internal */
 
 /* extra SELECTION_FILTER bits */
 #define SELECTION_FILTER_KEEP_IFEMPTY	(1 << 30)
 #define SELECTION_FILTER_SWAPPED	(1 << 31)
 
+/* internal */
+#define SELECTION_MATCH_SOLVABLE	(1 << 27)
+#define SELECTION_MODEBITS		(3 << 28)
 
 extern int  selection_make(Pool *pool, Queue *selection, const char *name, int flags);
 extern int  selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int flags, int keyname, int marker);
 extern int  selection_make_matchdepid(Pool *pool, Queue *selection, Id dep, int flags, int keyname, int marker);
+extern int selection_make_matchsolvable(Pool *pool, Queue *selection, Id solvid, int flags, int keyname, int marker);
+extern int selection_make_matchsolvablelist(Pool *pool, Queue *selection, Queue *solvidq, int flags, int keyname, int marker);
 
 extern void selection_filter(Pool *pool, Queue *sel1, Queue *sel2);
 extern void selection_add(Pool *pool, Queue *sel1, Queue *sel2);