Fix build error about binding python 2.7 path
[platform/upstream/libsolv.git] / src / selection.c
index 6ca72e5..a8e60f7 100644 (file)
 #include "selection.h"
 #include "solver.h"
 #include "evr.h"
+#ifdef ENABLE_CONDA
+#include "conda.h"
+#endif
 
+#ifdef _WIN32
+#include "strfncs.h"
+#endif
 
 static int
 str2archid(Pool *pool, const char *arch)
@@ -32,7 +38,7 @@ str2archid(Pool *pool, const char *arch)
   id = pool_str2id(pool, arch, 0);
   if (!id || id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
     return id;
-  if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
+  if (pool->id2arch && pool_arch2score(pool, id) == 0)
     return 0;
   return id;
 }
@@ -89,6 +95,7 @@ selection_solvables(Pool *pool, Queue *selection, Queue *pkgs)
   for (i = 0; i < selection->count; i += 2)
     {
       Id select = selection->elements[i] & SOLVER_SELECTMASK;
+      Id id = selection->elements[i + 1];
       if (select == SOLVER_SOLVABLE_ALL)
        {
          FOR_POOL_SOLVABLES(p)
@@ -97,16 +104,18 @@ selection_solvables(Pool *pool, Queue *selection, Queue *pkgs)
       if (select == SOLVER_SOLVABLE_REPO)
        {
          Solvable *s;
-         Repo *repo = pool_id2repo(pool, selection->elements[i + 1]);
+         Repo *repo = pool_id2repo(pool, id);
          if (repo)
            {
              FOR_REPO_SOLVABLES(repo, p, s)
                queue_push(pkgs, p);
            }
        }
+      else if (select == SOLVER_SOLVABLE)
+       queue_push(pkgs, id);
       else
        {
-         FOR_JOB_SELECT(p, pp, select, selection->elements[i + 1])
+         FOR_JOB_SELECT(p, pp, select, id)
            queue_push(pkgs, p);
        }
     }
@@ -141,6 +150,7 @@ selection_flatten(Pool *pool, Queue *selection)
   if (!q.count)
     {
       queue_empty(selection);
+      queue_free(&q);
       return;
     }
   queue_truncate(selection, 2);
@@ -154,6 +164,7 @@ selection_flatten(Pool *pool, Queue *selection)
       selection->elements[0] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
       selection->elements[1] = q.elements[0];
     }
+  queue_free(&q);
 }
 
 /* only supports simple rels plus REL_ARCH */
@@ -260,7 +271,7 @@ selection_filter_rel(Pool *pool, Queue *selection, Id relflags, Id relevr)
 /* limit a selection to to repository */
 /* prunes empty jobs */
 static void
-selection_filter_repo(Pool *pool, Queue *selection, Repo *repo)
+selection_filter_repo(Pool *pool, Queue *selection, Repo *repo, int setflags)
 {
   Queue q;
   int i, j;
@@ -285,7 +296,12 @@ selection_filter_repo(Pool *pool, Queue *selection, Repo *repo)
       else if (select == SOLVER_SOLVABLE_REPO)
        {
          if (id != repo->repoid)
-           select = 0;
+           continue;
+       }
+      else if (select == SOLVER_SOLVABLE)
+       {
+         if (pool->solvables[id].repo != repo)
+           continue;
        }
       else
        {
@@ -299,11 +315,11 @@ selection_filter_repo(Pool *pool, Queue *selection, Repo *repo)
              else
                queue_push(&q, p);
            }
-         if (bad || !q.count)
+         if (!q.count)
+           continue;
+         if (bad)
            {
-             if (!q.count)
-               select = 0;             /* prune empty jobs */
-             else if (q.count == 1)
+             if (q.count == 1)
                {
                  select = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
                  id = q.elements[0];
@@ -315,8 +331,6 @@ selection_filter_repo(Pool *pool, Queue *selection, Repo *repo)
                }
            }
        }
-      if (!select)
-       continue;       /* job is now empty */
       if (select == SOLVER_SOLVABLE_REPO)
        {
          Id p;
@@ -326,7 +340,7 @@ selection_filter_repo(Pool *pool, Queue *selection, Repo *repo)
          if (!p)
            continue;   /* repo is empty */
        }
-      selection->elements[j++] = select | (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SETREPO;
+      selection->elements[j++] = select | (selection->elements[i] & ~SOLVER_SELECTMASK) | setflags;
       selection->elements[j++] = id;
     }
   queue_truncate(selection, j);
@@ -538,8 +552,8 @@ selection_addextra_provides(Pool *pool, Queue *selection, const char *name, int
 }
 
 /* this is the fast path of selection_provides: the id for the name
- * is known and thus we can quickly check the existance of a
- * package with that provides */
+ * is known and thus we can use the whatprovides data to quickly
+ * check the existance of a package with that provides */
 static int
 selection_provides_id(Pool *pool, Queue *selection, Id id, int flags)
 {
@@ -550,20 +564,18 @@ selection_provides_id(Pool *pool, Queue *selection, Id id, int flags)
       Solvable *s = pool->solvables + p;
       if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
        continue;
-      break;
-    }
-  if (p)
-    {
       queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
       return SELECTION_PROVIDES;
     }
 
   if ((flags & (SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) != 0)
     {
+      /* misuse selection_addextra to test if there is an extra package
+       * that provides the id */
       queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
       selection_addextra(pool, selection, flags);
       if (selection->elements[0] == SOLVER_SOLVABLE_PROVIDES)
-       queue_empty(selection);
+       queue_empty(selection);         /* no extra package found */
       else
        {
           selection->elements[0] = SOLVER_SOLVABLE_PROVIDES;
@@ -575,7 +587,6 @@ selection_provides_id(Pool *pool, Queue *selection, Id id, int flags)
   return 0;
 }
 
-/* add missing provides matchers to the selection */
 /* match the provides of a package */
 /* note that we only return raw SOLVER_SOLVABLE_PROVIDES jobs
  * so that the selection can be modified later. */
@@ -619,7 +630,7 @@ selection_provides(Pool *pool, Queue *selection, const char *name, int flags)
   for (id = 1; id < pool->ss.nstrings; id++)
     {
       /* do we habe packages providing this id? */
-      if (!pool->whatprovides[id] || pool->whatprovides[id] == 1)
+      if ((!pool->whatprovides[id] && pool->addedfileprovides == 2) || pool->whatprovides[id] == 1)
        continue;
       n = pool_id2str(pool, id);
       if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0)
@@ -632,6 +643,13 @@ selection_provides(Pool *pool, Queue *selection, const char *name, int flags)
              if (!p)
                continue;
            }
+         else if (!pool->whatprovides[id])
+           {
+             FOR_PROVIDES(p, pp, id)
+               break;
+             if (!p)
+               continue;
+           }
          queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
          match = 1;
        }
@@ -704,6 +722,34 @@ selection_name_id(Pool *pool, Queue *selection, Id id, int flags)
   return 0;
 }
 
+/* does not check SELECTION_INSTALLED_ONLY, as it is normally done
+ * by other means */
+static inline int
+solvable_matches_selection_flags(Pool *pool, Solvable *s, int flags)
+{
+  if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+    {
+      if (!(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE))
+       return 0;
+      /* source package are never installed and never have a bad arch */
+      if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s))
+       return 0;
+    }
+  else
+    {
+      if ((flags & SELECTION_SOURCE_ONLY) != 0)
+       return 0;
+      if (s->repo != pool->installed)
+       {
+         if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s))
+           return 0;
+         if (!(flags & SELECTION_WITH_BADARCH) && pool_badarch_solvable(pool, s))
+           return 0;
+       }
+    }
+  return 1;
+}
+
 /* match the name of a package */
 /* note that for SELECTION_INSTALLED_ONLY the result is not trimmed */
 static int
@@ -743,20 +789,8 @@ selection_name(Pool *pool, Queue *selection, const char *name, int flags)
       Solvable *s = pool->solvables + p;
       if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
        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 (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 (!solvable_matches_selection_flags(pool, s, flags))
+       continue;
       id = s->name;
       n = pool_id2str(pool, id);
       if (flags & SELECTION_SKIP_KIND)
@@ -940,31 +974,14 @@ selection_filelist(Pool *pool, Queue *selection, const char *name, int flags)
   if ((flags & SELECTION_NOCASE) != 0)
     type |= SEARCH_NOCASE;
   queue_init(&q);
-  dataiterator_init(&di, pool, flags & SELECTION_INSTALLED_ONLY ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
+  dataiterator_init(&di, pool, flags & SELECTION_INSTALLED_ONLY ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES);
   while (dataiterator_step(&di))
     {
       Solvable *s = pool->solvables + di.solvid;
       if (!s->repo)
        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 (!solvable_matches_selection_flags(pool, s, flags))
+       continue;
       if ((flags & SELECTION_FLAT) != 0)
        {
          /* don't bother with the complex stuff */
@@ -1140,6 +1157,19 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
   flags |= SELECTION_NAME;
   flags &= ~SELECTION_PROVIDES;
 
+#ifdef ENABLE_CONDA
+  if (pool->disttype == DISTTYPE_CONDA)
+    {
+      Id *wp, id = pool_conda_matchspec(pool, name);
+      if (!id)
+       return 0;
+      wp = pool_whatprovides_ptr(pool, id);         /* check if there is a match */
+      if (!wp || !*wp)
+       return 0;
+      queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
+      return SELECTION_CANON;
+    }
+#endif
   if (pool->disttype == DISTTYPE_DEB)
     {
       if ((r = strchr(name, '_')) == 0)
@@ -1291,46 +1321,57 @@ selection_extrabits(Pool *pool, Queue *selection, int flags)
   return needflags;
 }
 
+static int
+selection_combine(Pool *pool, Queue *sel1, Queue *sel2, int flags, int ret)
+{
+  if ((flags & SELECTION_MODEBITS) == SELECTION_ADD)
+    selection_add(pool, sel1, sel2);
+  else if ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT)
+    selection_subtract(pool, sel1, sel2);
+  else if ((flags & SELECTION_MODEBITS) == SELECTION_FILTER)
+    {
+      if (ret || !(flags & SELECTION_FILTER_KEEP_IFEMPTY))
+       {
+         if ((flags & SELECTION_FILTER_SWAPPED) != 0)
+           {
+             selection_filter(pool, sel2, sel1);
+             queue_free(sel1);
+             queue_init_clone(sel1, sel2);
+           }
+         else
+           selection_filter(pool, sel1, sel2);
+       }
+    }
+  else /* SELECTION_REPLACE */
+    {
+      queue_free(sel1);
+      queue_init_clone(sel1, sel2);
+    }
+  queue_free(sel2);
+  return ret;
+}
+
 int
 selection_make(Pool *pool, Queue *selection, const char *name, int flags)
 {
   int ret = 0;
-  if ((flags & SELECTION_MODEBITS) != 0)
+  if ((flags & SELECTION_MODEBITS) != SELECTION_REPLACE)
     {
       Queue q;
 
-      queue_init(&q);
       if ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT || (flags & SELECTION_MODEBITS) == SELECTION_FILTER)
        {
          if (!selection->count)
-           {
-             queue_free(&q);
-             return 0;
-           }
+           return 0;
          if ((flags & (SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH | SELECTION_WITH_SOURCE)) != 0)
            {
              /* try to drop expensive extra bits */
              flags = (flags & ~(SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH | SELECTION_WITH_SOURCE)) | selection_extrabits(pool, selection, flags);
            }
        }
+      queue_init(&q);
       ret = selection_make(pool, &q, name, flags & ~SELECTION_MODEBITS);
-      if ((flags & SELECTION_MODEBITS) == SELECTION_ADD)
-       selection_add(pool, selection, &q);
-      else if ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT)
-       selection_subtract(pool, selection, &q);
-      else if (ret || !(flags & SELECTION_FILTER_KEEP_IFEMPTY))
-       {
-         if ((flags & SELECTION_FILTER_SWAPPED) != 0)
-           {
-             selection_filter(pool, &q, selection);
-             queue_free(selection);
-             queue_init_clone(selection, &q);
-           }
-         else
-           selection_filter(pool, selection, &q);
-       }
-      queue_free(&q);
-      return ret;
+      return selection_combine(pool, selection, &q, flags, ret);
     }
   queue_empty(selection);
   if ((flags & SELECTION_INSTALLED_ONLY) != 0 && !pool->installed)
@@ -1348,7 +1389,7 @@ selection_make(Pool *pool, Queue *selection, const char *name, int flags)
 
   /* now do result filtering */
   if (ret && (flags & SELECTION_INSTALLED_ONLY) != 0)
-    selection_filter_repo(pool, selection, pool->installed);
+    selection_filter_repo(pool, selection, pool->installed, SOLVER_SETREPO);
 
   /* flatten if requested */
   if (ret && (flags & SELECTION_FLAT) != 0)
@@ -1356,33 +1397,11 @@ selection_make(Pool *pool, Queue *selection, const char *name, int flags)
   return selection->count ? ret : 0;
 }
 
-struct limiter {
-  int start;   /* either 2 or repofilter->start */
-  int end;     /* either nsolvables or repofilter->end */
-  Id *mapper;
-  Repo *repofilter;
-};
-
-/* add matching src packages to simple SOLVABLE_NAME selections */
-static void
-setup_limiter(Pool *pool, int flags, struct limiter *limiter)
-{
-  limiter->start = 2;
-  limiter->end = pool->nsolvables;
-  limiter->mapper = 0;
-  limiter->repofilter = 0;
-  if ((flags & SELECTION_INSTALLED_ONLY) != 0)
-    {
-      Repo *repo = pool->installed;
-      limiter->repofilter = repo;
-      limiter->start = repo ? repo->start : 0;
-      limiter->end = repo ? repo->end : 0;
-    }
-}
-
 static int
 matchdep_str(const char *pattern, const char *string, int flags)
 {
+  if (!pattern || !string)
+    return 0;
   if (flags & SELECTION_GLOB)
     {
       int globflags = (flags & SELECTION_NOCASE) != 0 ? FNM_CASEFOLD : 0;
@@ -1428,8 +1447,88 @@ matchdep(Pool *pool, Id id, char *rname, int rflags, Id revr, int flags)
   return matchdep_str(rname, pool_id2str(pool, id), flags);
 }
 
+struct limiter {
+  int start;   /* either 2 or repofilter->start */
+  int end;     /* either nsolvables or repofilter->end */
+  Repo *repofilter;
+  Id *mapper;
+  Queue qlimit;
+};
+
+
+static int
+selection_make_matchsolvable_common(Pool *pool, Queue *selection, Queue *solvidq, Id solvid, int flags, int keyname, int marker, struct limiter *limiter)
+{
+  Map m, missc;
+  int reloff;
+  int li, i, j;
+  Id p;
+  Queue q;
+
+  if ((flags & SELECTION_MODEBITS) != SELECTION_REPLACE)
+    {
+      int ret;
+      Queue q;
+      queue_init(&q);
+      ret = selection_make_matchsolvable_common(pool, &q, solvidq, solvid, flags & ~SELECTION_MODEBITS, keyname, marker, limiter);
+      return selection_combine(pool, selection, &q, flags, ret);
+    }
+
+  queue_empty(selection);
+  if (!limiter->end)
+    return 0;
+  if (!solvidq && !solvid)
+    return 0;
+  if (solvidq && solvid)
+    return 0;
+
+  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 (!solvable_matches_selection_flags(pool, s, flags))
+       continue;
+      if (solvable_matchessolvable_int(s, keyname, marker, solvid, solvidq ? &m : 0, &q, &missc, reloff, 0))
+        queue_push(selection, p);
+    }
+  queue_free(&q);
+  map_free(&missc);
+  if (solvidq)
+    map_free(&m);
+
+  /* convert package list to selection */
+  if (!selection->count)
+    return 0;
+  j = selection->count;
+  queue_insertn(selection, 0, selection->count, 0);
+  for (i = 0; i < selection->count; i += 2)
+    {
+      selection->elements[i] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
+      selection->elements[i + 1] = 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)
+selection_make_matchdeps_common(Pool *pool, Queue *selection, const char *name, Id dep, int flags, int keyname, int marker, struct limiter *limiter)
 {
   int li, i, j;
   int ret = 0;
@@ -1439,11 +1538,21 @@ selection_make_matchdeps_common_limited(Pool *pool, Queue *selection, const char
   Id p;
   Queue q;
 
+  if ((flags & SELECTION_MODEBITS) != SELECTION_REPLACE)
+    {
+      Queue q;
+      queue_init(&q);
+      ret = selection_make_matchdeps_common(pool, &q, name, dep, flags & ~SELECTION_MODEBITS, keyname, marker, limiter);
+      return selection_combine(pool, selection, &q, flags, ret);
+    }
+
   queue_empty(selection);
   if (!limiter->end)
     return 0;
   if (!name && !dep)
     return 0;
+  if (name && dep)
+    return 0;
 
   if ((flags & SELECTION_MATCH_DEPSTR) != 0)
     flags &= ~SELECTION_REL;
@@ -1464,7 +1573,7 @@ selection_make_matchdeps_common_limited(Pool *pool, Queue *selection, const char
          revr = pool_str2id(pool, r, 1);
          ret |= SELECTION_REL;
        }
-      if ((flags & SELECTION_GLOB) != 0 && !strpbrk(rname, "[*?") != 0)
+      if ((flags & SELECTION_GLOB) != 0 && strpbrk(rname, "[*?") == 0)
        flags &= ~SELECTION_GLOB;
 
       if ((flags & SELECTION_GLOB) == 0 && (flags & SELECTION_NOCASE) == 0 && (flags & SELECTION_MATCH_DEPSTR) == 0)
@@ -1501,25 +1610,8 @@ selection_make_matchdeps_common_limited(Pool *pool, Queue *selection, const char
       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 (!solvable_matches_selection_flags(pool, s, flags))
+       continue;
       if (keyname == SOLVABLE_NAME)                    /* nevr match hack */
        {
          if (dep)
@@ -1554,7 +1646,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;
@@ -1593,16 +1686,16 @@ selection_make_matchdeps_common_limited(Pool *pool, Queue *selection, const char
     }
   queue_free(&q);
   solv_free(rname);
-  if (!selection->count)
-    return 0;
 
   /* convert package list to selection */
+  if (!selection->count)
+    return 0;
   j = selection->count;
   queue_insertn(selection, 0, selection->count, 0);
-  for (i = 0; i < selection->count; )
+  for (i = 0; i < selection->count; i += 2)
     {
-      selection->elements[i++] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
-      selection->elements[i++] = selection->elements[j++];
+      selection->elements[i] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
+      selection->elements[i + 1] = selection->elements[j++];
     }
 
   if ((flags & SELECTION_FLAT) != 0)
@@ -1610,75 +1703,56 @@ selection_make_matchdeps_common_limited(Pool *pool, Queue *selection, const char
   return ret | (keyname == SOLVABLE_NAME ? SELECTION_NAME : SELECTION_PROVIDES);
 }
 
-static int
-selection_make_matchdeps_common(Pool *pool, Queue *selection, const char *name, Id dep, int flags, int keyname, int marker)
+static void
+setup_limiter(Pool *pool, Queue *selection, int flags, struct limiter *limiter)
 {
-  struct limiter limiter;
-
-  setup_limiter(pool, flags, &limiter);
-  if ((flags & SELECTION_MODEBITS) != SELECTION_REPLACE)
+  limiter->start = 2;
+  limiter->end = pool->nsolvables;
+  limiter->mapper = 0;
+  limiter->repofilter = 0;
+  if ((flags & SELECTION_INSTALLED_ONLY) != 0)
     {
-      int ret;
-      Queue q, qlimit;
-      queue_init(&q);
-      queue_init(&qlimit);
-      /* deal with special filter cases */
-      if ((flags & SELECTION_MODEBITS) == SELECTION_FILTER && selection->count == 2 && limiter.end)
-       {
-         if ((selection->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL)
-           flags = (flags & ~SELECTION_MODEBITS) | SELECTION_REPLACE;
-         else if ((selection->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_REPO)
-           {
-             Repo *repo = pool_id2repo(pool, selection->elements[1]);
-             if (limiter.repofilter && repo != limiter.repofilter)
-               repo = 0;
-             limiter.repofilter = repo;
-             limiter.start = repo ? repo->start : 0;
-             limiter.end = repo ? repo->end : 0;
-             flags = (flags & ~SELECTION_MODEBITS) | SELECTION_REPLACE;
-           }
-       }
-      if (limiter.end && ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT || (flags & SELECTION_MODEBITS) == SELECTION_FILTER))
-       {
-         selection_solvables(pool, selection, &qlimit);
-         limiter.start = 0;
-         limiter.end = qlimit.count;
-         limiter.mapper = qlimit.elements;
-       }
-      ret = selection_make_matchdeps_common_limited(pool, &q, name, dep, flags & ~SELECTION_MODEBITS, keyname, marker, &limiter);
-      queue_free(&qlimit);
-      if ((flags & SELECTION_MODEBITS) == SELECTION_ADD)
-       selection_add(pool, selection, &q);
-      else if ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT)
-       selection_subtract(pool, selection, &q);
-      else if ((flags & SELECTION_MODEBITS) == SELECTION_FILTER)
-       {
-          if (ret || !(flags & SELECTION_FILTER_KEEP_IFEMPTY))
-           {
-             if ((flags & SELECTION_FILTER_SWAPPED) != 0)
-               {
-                 selection_filter(pool, &q, selection);
-                 queue_free(selection);
-                 queue_init_clone(selection, &q);
-               }
-             else
-               selection_filter(pool, selection, &q);
-           }
-       }
-      else if (ret || !(flags & SELECTION_FILTER_KEEP_IFEMPTY))
-       {
-         /* special filter case from above */
-         int i;
-         Id f = selection->elements[0] & ~(SOLVER_SELECTMASK|SOLVER_NOAUTOSET);        /* job, jobflags, setflags */
-         queue_free(selection);
-         queue_init_clone(selection, &q);
-         for (i = 0; i < selection->count; i += 2)
-           selection->elements[i] = (selection->elements[i] & (SOLVER_SELECTMASK | SOLVER_SETMASK)) | f;
-       }
-      queue_free(&q);
-      return ret;
+      Repo *repo = pool->installed;
+      limiter->repofilter = repo;
+      limiter->start = repo ? repo->start : 0;
+      limiter->end = repo ? repo->end : 0;
+    }
+  if ((flags & SELECTION_MODEBITS) != SELECTION_SUBTRACT && (flags & SELECTION_MODEBITS) != SELECTION_FILTER)
+    return;
+  /* the result will be limited to the first selection */
+  if (!selection->count)
+    limiter->start = limiter->end = 0;
+  if (!limiter->end)
+    return;
+  /* check for special cases where we do not need to call selection_solvables() */
+  if (selection->count == 2 && (selection->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL)
+    return;
+  if (selection->count == 2 && (selection->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_REPO)
+    {
+      Repo *repo = pool_id2repo(pool, selection->elements[1]);
+      if (limiter->repofilter && repo != limiter->repofilter)
+       repo = 0;
+      limiter->repofilter = repo;
+      limiter->start = repo ? repo->start : 0;
+      limiter->end = repo ? repo->end : 0;
+      return;
     }
-  return selection_make_matchdeps_common_limited(pool, selection, name, dep, flags, keyname, marker, &limiter);
+  /* convert selection into a package list and use it in the limiter */
+  queue_init(&limiter->qlimit);
+  selection_solvables(pool, selection, &limiter->qlimit);
+  limiter->start = 0;
+  limiter->end = limiter->qlimit.count;
+  if (!limiter->qlimit.count)
+    queue_free(&limiter->qlimit);
+  else
+    limiter->mapper = limiter->qlimit.elements;
+}
+
+static void
+free_limiter(struct limiter *limiter)
+{
+  if (limiter->mapper)
+    queue_free(&limiter->qlimit);
 }
 
 /*
@@ -1692,7 +1766,12 @@ selection_make_matchdeps_common(Pool *pool, Queue *selection, const char *name,
 int
 selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int flags, int keyname, int marker)
 {
-  return selection_make_matchdeps_common(pool, selection, name, 0, flags, keyname, marker);
+  struct limiter limiter;
+  int ret;
+  setup_limiter(pool, selection, flags, &limiter);
+  ret = selection_make_matchdeps_common(pool, selection, name, 0, flags, keyname, marker, &limiter);
+  free_limiter(&limiter);
+  return ret;
 }
 
 /*
@@ -1701,7 +1780,34 @@ selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int fla
 int
 selection_make_matchdepid(Pool *pool, Queue *selection, Id dep, int flags, int keyname, int marker)
 {
-  return selection_make_matchdeps_common(pool, selection, 0, dep, flags, keyname, marker);
+  struct limiter limiter;
+  int ret;
+  setup_limiter(pool, selection, flags, &limiter);
+  ret = selection_make_matchdeps_common(pool, selection, 0, dep, flags, keyname, marker, &limiter);
+  free_limiter(&limiter);
+  return ret;
+}
+
+int
+selection_make_matchsolvable(Pool *pool, Queue *selection, Id solvid, int flags, int keyname, int marker)
+{
+  struct limiter limiter;
+  int ret;
+  setup_limiter(pool, selection, flags, &limiter);
+  ret = selection_make_matchsolvable_common(pool, selection, 0, solvid, flags, keyname, marker, &limiter);
+  free_limiter(&limiter);
+  return ret;
+}
+
+int
+selection_make_matchsolvablelist(Pool *pool, Queue *selection, Queue *solvidq, int flags, int keyname, int marker)
+{
+  struct limiter limiter;
+  int ret;
+  setup_limiter(pool, selection, flags, &limiter);
+  ret = selection_make_matchsolvable_common(pool, selection, solvidq, 0, flags, keyname, marker, &limiter);
+  free_limiter(&limiter);
+  return ret;
 }
 
 static inline int
@@ -1738,7 +1844,9 @@ selection_filter_map(Pool *pool, Queue *sel, Map *m, int setflags)
   for (i = j = 0; i < sel->count; i += 2)
     {
       Id select = sel->elements[i] & SOLVER_SELECTMASK;
-      queue_empty(&q);
+      Id id = sel->elements[i + 1];
+      if (q.count)
+        queue_empty(&q);
       miss = 0;
       if (select == SOLVER_SOLVABLE_ALL)
        {
@@ -1753,7 +1861,7 @@ selection_filter_map(Pool *pool, Queue *sel, Map *m, int setflags)
       else if (select == SOLVER_SOLVABLE_REPO)
        {
          Solvable *s;
-         Repo *repo = pool_id2repo(pool, sel->elements[i + 1]);
+         Repo *repo = pool_id2repo(pool, id);
          if (repo)
            {
              FOR_REPO_SOLVABLES(repo, p, s)
@@ -1765,9 +1873,18 @@ selection_filter_map(Pool *pool, Queue *sel, Map *m, int setflags)
                }
            }
        }
+      else if (select == SOLVER_SOLVABLE)
+       {
+         if (!map_tst(m, id))
+           continue;
+         sel->elements[j] = sel->elements[i] | setflags;
+         sel->elements[j + 1] = id;
+          j += 2;
+         continue;
+       }
       else
        {
-         FOR_JOB_SELECT(p, pp, select, sel->elements[i + 1])
+         FOR_JOB_SELECT(p, pp, select, id)
            {
              if (map_tst(m, p))
                queue_pushunique(&q, p);
@@ -1780,7 +1897,7 @@ selection_filter_map(Pool *pool, Queue *sel, Map *m, int setflags)
       if (!miss)
        {
          sel->elements[j] = sel->elements[i] | setflags;
-         sel->elements[j + 1] = sel->elements[i + 1];
+         sel->elements[j + 1] = id;
        }
       else if (q.count > 1)
        {
@@ -1807,6 +1924,7 @@ selection_filter_int(Pool *pool, Queue *sel1, Queue *sel2, int invert)
   Map m2;
   Id setflags = 0;
 
+  /* handle special cases */
   if (!sel1->count || !sel2->count)
     {
       if (invert && !sel2->count)
@@ -1817,11 +1935,35 @@ selection_filter_int(Pool *pool, Queue *sel1, Queue *sel2, int invert)
   if (sel1->count == 2 && (sel1->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL && !invert)
     {
       /* XXX: not 100% correct, but very useful */
-      p = sel1->elements[0] & ~(SOLVER_SELECTMASK | SOLVER_SETMASK);   /* job & jobflags */
+      setflags = sel1->elements[0] & ~(SOLVER_SELECTMASK | SOLVER_SETMASK);    /* job & jobflags */
+      queue_free(sel1);
+      queue_init_clone(sel1, sel2);
+      for (i = 0; i < sel1->count; i += 2)
+        sel1->elements[i] = (sel1->elements[i] & (SOLVER_SELECTMASK | SOLVER_SETMASK)) | setflags;
+      return;
+    }
+  if (sel1->count == 2 && (sel1->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_REPO && !invert)
+    {
+      Repo *repo = pool_id2repo(pool, sel1->elements[1]);
+      setflags = sel1->elements[0] & ~(SOLVER_SELECTMASK | SOLVER_NOAUTOSET);  /* job, jobflags, setflags */
       queue_free(sel1);
       queue_init_clone(sel1, sel2);
       for (i = 0; i < sel1->count; i += 2)
-        sel1->elements[i] = (sel1->elements[i] & (SOLVER_SELECTMASK | SOLVER_SETMASK)) | p ;
+        sel1->elements[i] &= SOLVER_SELECTMASK | SOLVER_SETMASK;       /* remove job and jobflags */
+      selection_filter_repo(pool, sel1, repo, setflags);
+      return;
+    }
+  if (sel2->count == 2 && (sel2->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL)
+    {
+      if (invert)
+       queue_empty(sel1);
+      return;
+    }
+  if (sel2->count == 2 && (sel2->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_REPO && !invert)
+    {
+      Repo *repo = pool_id2repo(pool, sel2->elements[1]);
+      setflags = sel2->elements[0] & (SOLVER_SETMASK & ~SOLVER_NOAUTOSET);
+      selection_filter_repo(pool, sel1, repo, setflags);
       return;
     }
 
@@ -1893,7 +2035,7 @@ selection_filter_int(Pool *pool, Queue *sel1, Queue *sel2, int invert)
   if (invert)
     map_invertall(&m2);
   if (sel2->count == 2)
-    setflags = sel2->elements[0] & SOLVER_SETMASK & ~SOLVER_NOAUTOSET;
+    setflags = sel2->elements[0] & (SOLVER_SETMASK & ~SOLVER_NOAUTOSET);
   selection_filter_map(pool, sel1, &m2, setflags);
   map_free(&m2);
 }