Imported Upstream version 0.7.8
[platform/upstream/libsolv.git] / src / selection.c
index 979c897..5f01e2b 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,11 +38,12 @@ 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;
 }
 
+/* remove empty jobs from the selection */
 static void
 selection_prune(Pool *pool, Queue *selection)
 {
@@ -53,8 +60,10 @@ selection_prune(Pool *pool, Queue *selection)
          Solvable *s;
          Repo *repo = pool_id2repo(pool, selection->elements[i + 1]);
          if (repo)
-           FOR_REPO_SOLVABLES(repo, p, s)
-             break;
+           {
+             FOR_REPO_SOLVABLES(repo, p, s)
+               break;
+           }
        }
       else
        {
@@ -86,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)
@@ -94,14 +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);
+           {
+             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);
        }
     }
@@ -151,33 +165,49 @@ selection_flatten(Pool *pool, Queue *selection)
     }
 }
 
+/* only supports simple rels plus REL_ARCH */
+static int
+match_nevr_rel(Pool *pool, Solvable *s, Id rflags, Id revr)
+{
+  if (rflags == REL_ARCH)
+    {
+      if (s->arch != revr) 
+       {
+         if (revr != ARCH_SRC || s->arch != ARCH_NOSRC)
+           return 0;
+       }
+      return 1;
+    }
+  if (rflags > 7)
+    return 0;
+  return pool_intersect_evrs(pool, REL_EQ, s->evr, rflags, revr);
+}
+
+/* only supports simple rels plus REL_ARCH */
 static void
-selection_filter_rel(Pool *pool, Queue *selection, Id relflags, Id relevr)
+selection_filter_rel_noprune(Pool *pool, Queue *selection, Id relflags, Id relevr)
 {
   int i;
 
+  if (!selection->count)
+    return;
+
   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 || select == SOLVER_SOLVABLE_ONE_OF)
        {
-         /* done by selection_addsrc */
+         /* done by selection_addextra, currently implies SELECTION_NAME */
          Queue q;
          Id p, pp;
-         Id rel = 0, relname = 0;
          int miss = 0;
 
          queue_init(&q);
          FOR_JOB_SELECT(p, pp, select, id)
            {
              Solvable *s = pool->solvables + p;
-             if (!rel || s->name != relname)
-               {
-                 relname = s->name;
-                 rel = pool_rel2id(pool, relname, relevr, relflags, 1);
-               }
-             if (pool_match_nevr(pool, s, rel))
+             if (match_nevr_rel(pool, s, relflags, relevr))
                queue_push(&q, p);
              else
                miss = 1;
@@ -209,362 +239,806 @@ selection_filter_rel(Pool *pool, Queue *selection, Id relflags, Id relevr)
          selection->elements[i + 1] = pool_rel2id(pool, id, relevr, relflags, 1);
        }
       else
-       continue;       /* actually internal error */
+       continue;       /* actually cannot happen */
+
+      /* now add the setflags we gained */
       if (relflags == REL_ARCH)
-        selection->elements[i] |= SOLVER_SETARCH;
+       selection->elements[i] |= SOLVER_SETARCH;
       if (relflags == REL_EQ && select != SOLVER_SOLVABLE_PROVIDES)
-        {
+       {
          if (pool->disttype == DISTTYPE_DEB)
-            selection->elements[i] |= SOLVER_SETEVR;   /* debian can't match version only like rpm */
+           selection->elements[i] |= SOLVER_SETEVR;    /* debian can't match version only like rpm */
          else
            {
              const char *rel =  strrchr(pool_id2str(pool, relevr), '-');
              selection->elements[i] |= rel ? SOLVER_SETEVR : SOLVER_SETEV;
            }
-        }
+       }
     }
+}
+
+/* only supports simple rels plus REL_ARCH */
+/* prunes empty jobs */
+static void
+selection_filter_rel(Pool *pool, Queue *selection, Id relflags, Id relevr)
+{
+  selection_filter_rel_noprune(pool, selection, relflags, relevr);
   selection_prune(pool, selection);
 }
 
+/* limit a selection to to repository */
+/* prunes empty jobs */
+static void
+selection_filter_repo(Pool *pool, Queue *selection, Repo *repo, int setflags)
+{
+  Queue q;
+  int i, j;
+
+  if (!selection->count)
+    return;
+  if (!repo)
+    {
+      queue_empty(selection);
+      return;
+    }
+  queue_init(&q);
+  for (i = j = 0; i < selection->count; i += 2)
+    {
+      Id select = selection->elements[i] & SOLVER_SELECTMASK;
+      Id id = selection->elements[i + 1];
+      if (select == SOLVER_SOLVABLE_ALL)
+       {
+         select = SOLVER_SOLVABLE_REPO;
+         id = repo->repoid;
+       }
+      else if (select == SOLVER_SOLVABLE_REPO)
+       {
+         if (id != repo->repoid)
+           continue;
+       }
+      else if (select == SOLVER_SOLVABLE)
+       {
+         if (pool->solvables[id].repo != repo)
+           continue;
+       }
+      else
+       {
+         int bad = 0;
+         Id p, pp;
+         queue_empty(&q);
+         FOR_JOB_SELECT(p, pp, select, id)
+           {
+             if (pool->solvables[p].repo != repo)
+               bad = 1;
+             else
+               queue_push(&q, p);
+           }
+         if (!q.count)
+           continue;
+         if (bad)
+           {
+             if (q.count == 1)
+               {
+                 select = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
+                 id = q.elements[0];
+               }
+             else
+               {
+                 select = SOLVER_SOLVABLE_ONE_OF;
+                 id = pool_queuetowhatprovides(pool, &q);
+               }
+           }
+       }
+      if (select == SOLVER_SOLVABLE_REPO)
+       {
+         Id p;
+         Solvable *s;
+         FOR_REPO_SOLVABLES(repo, p, s)
+           break;
+         if (!p)
+           continue;   /* repo is empty */
+       }
+      selection->elements[j++] = select | (selection->elements[i] & ~SOLVER_SELECTMASK) | setflags;
+      selection->elements[j++] = id;
+    }
+  queue_truncate(selection, j);
+  queue_free(&q);
+}
+
+
+static int
+matchprovides(Pool *pool, Solvable *s, Id dep)
+{
+  Id id, *idp;
+  idp = s->repo->idarraydata + s->provides;
+  while ((id = *idp++) != 0)
+    if (pool_match_dep(pool, id, dep))
+      return 1;
+  return 0;
+}
+
+/* change a SOLVER_SOLVABLE_NAME/PROVIDES selection to something that also includes
+ * extra packages.
+ * extra packages are: src, badarch, disabled
+ */
 static void
-selection_addsrc(Pool *pool, Queue *selection, int flags)
+selection_addextra(Pool *pool, Queue *selection, int flags)
 {
   Queue q;
-  Id p, name;
-  int i, havesrc;
+  Id p, pp, dep;
+  int i, isextra, haveextra, doprovides;
 
   if ((flags & SELECTION_INSTALLED_ONLY) != 0)
-    return;    /* sources can't be installed */
+    flags &= ~SELECTION_WITH_SOURCE;
+
+  if (!(flags & (SELECTION_WITH_SOURCE | SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH)))
+    return;    /* nothing to add */
+
   queue_init(&q);
   for (i = 0; i < selection->count; i += 2)
     {
-      if (selection->elements[i] != SOLVER_SOLVABLE_NAME)
+      if (selection->elements[i] == SOLVER_SOLVABLE_NAME)
+       doprovides = 0;
+      else if (selection->elements[i] == SOLVER_SOLVABLE_PROVIDES)
+       doprovides = 1;
+      else
        continue;
-      name = selection->elements[i + 1];
-      havesrc = 0;
+      dep = selection->elements[i + 1];
+      haveextra = 0;
       queue_empty(&q);
+      if (doprovides)
+       {
+         /* first put all non-extra packages on the queue */
+         FOR_PROVIDES(p, pp, dep)
+           {
+             if ((flags & SELECTION_INSTALLED_ONLY) != 0 && pool->solvables[p].repo != pool->installed)
+               continue;
+             queue_push(&q, p);
+           }
+       }
       FOR_POOL_SOLVABLES(p)
        {
          Solvable *s = pool->solvables + p;
-         if (s->name != name)
+         if (!doprovides && !pool_match_nevr(pool, s, dep))
            continue;
+         if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+           continue;
+         isextra = 0;
          if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
            {
+             if (!(flags & SELECTION_WITH_SOURCE) && !(flags & SELECTION_SOURCE_ONLY))
+               continue;
+             if (!(flags & SELECTION_SOURCE_ONLY))
+               isextra = 1;
              if (pool_disabled_solvable(pool, s))
+               {
+                 if (!(flags & SELECTION_WITH_DISABLED))
+                   continue;
+                 isextra = 1;
+               }
+           }
+         else
+           {
+             if ((flags & SELECTION_SOURCE_ONLY) != 0)
+               continue;
+             if (s->repo != pool->installed)
+               {
+                 if (pool_disabled_solvable(pool, s))
+                   {
+                     if (!(flags & SELECTION_WITH_DISABLED))
+                       continue;
+                     isextra = 1;
+                   }
+                 if (pool_badarch_solvable(pool, s))
+                   {
+                     if (!(flags & SELECTION_WITH_BADARCH))
+                       continue;
+                     isextra = 1;
+                   }
+               }
+           }
+         if (doprovides)
+           {
+             if (!isextra)
+               continue;       /* already done above in FOR_PROVIDES */
+             if (!s->provides || !matchprovides(pool, s, dep))
                continue;
-             havesrc = 1;
            }
-         else if (s->repo != pool->installed && !pool_installable(pool, s))
-           continue;
+         haveextra |= isextra;
          queue_push(&q, p);
        }
-      if (!havesrc || !q.count)
+      if (!haveextra || !q.count)
        continue;
       if (q.count == 1)
        {
-         selection->elements[i] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
+         selection->elements[i] = (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
          selection->elements[i + 1] = q.elements[0];
        }
       else
        {
-         selection->elements[i] = SOLVER_SOLVABLE_ONE_OF;
+         if (doprovides)
+           solv_sort(q.elements, q.count, sizeof(Id), selection_solvables_sortcmp, NULL);
+         selection->elements[i] = (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF;
          selection->elements[i + 1] = pool_queuetowhatprovides(pool, &q);
        }
     }
   queue_free(&q);
 }
 
-static int
-selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
+static inline const char *
+skipkind(const char *n)
 {
-  Id id, p, pp;
-  int i, match = 0;
-  int doglob = 0;
-  int globflags = 0;
+  const char *s;
+  for (s = n; *s >= 'a' && *s <= 'z'; s++)
+    ;
+  if (*s == ':' && s != n)
+     return s + 1;
+  return n;
+}
 
-  if ((flags & SELECTION_SOURCE_ONLY) != 0)
-    {
-      flags &= ~SELECTION_PROVIDES;    /* sources don't provide anything */
-      flags &= ~SELECTION_WITH_SOURCE;
-    }
+static inline void
+queue_pushunique2(Queue *q, Id id1, Id id2)
+{
+  int i;
+  for (i = 0; i < q->count; i += 2)
+    if (q->elements[i] == id1 && q->elements[i + 1] == id2)
+      return;
+  queue_push2(q, id1, id2);
+}
 
-  if (!(flags & (SELECTION_NAME|SELECTION_PROVIDES)))
-    return 0;
 
-  if ((flags & SELECTION_INSTALLED_ONLY) != 0 && !pool->installed)
-    return 0;
+/*****  provides matching  *****/
 
-  if (!(flags & SELECTION_NOCASE))
+static int
+selection_addextra_provides(Pool *pool, Queue *selection, const char *name, int flags)
+{
+  Id p, id, *idp;
+  int match = 0;
+  int doglob, nocase, globflags;
+
+  if ((flags & SELECTION_INSTALLED_ONLY) != 0)
+    return 0;  /* neither disabled nor badarch nor src */
+
+  nocase = flags & SELECTION_NOCASE;
+  doglob = (flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0;
+  globflags = doglob && nocase ? FNM_CASEFOLD : 0;
+
+  FOR_POOL_SOLVABLES(p)
     {
-      id = pool_str2id(pool, name, 0);
-      if (id)
+      const char *n;
+      Solvable *s = pool->solvables + p;
+      if (!s->provides)
+       continue;
+      if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)        /* no provides */
+       continue;
+      if (s->repo == pool->installed)
+       continue;
+      if (pool_disabled_solvable(pool, s))
+       {
+         if (!(flags & SELECTION_WITH_DISABLED))
+           continue;
+         if (!(flags & SELECTION_WITH_BADARCH) && pool_badarch_solvable(pool, s))
+           continue;
+       }
+      else if (pool_badarch_solvable(pool, s))
+       {
+         if (!(flags & SELECTION_WITH_BADARCH))
+           continue;
+       }
+      else
+       continue;
+      /* here is an extra solvable we need to consider */
+      idp = s->repo->idarraydata + s->provides;
+      while ((id = *idp++) != 0)
        {
-         if ((flags & (SELECTION_SOURCE_ONLY | SELECTION_WITH_SOURCE)) != 0 && (flags & SELECTION_NAME) != 0)
+         while (ISRELDEP(id))
            {
-             /* src rpms don't have provides, so we must check every solvable */
-             FOR_PROVIDES(p, pp, id)   /* try fast path first */
-               {
-                 Solvable *s = pool->solvables + p;
-                 if (s->name == id)
-                   {
-                     if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
-                       continue;
-                     if ((flags & SELECTION_SOURCE_ONLY) != 0)
-                       id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
-                     queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
-                     if ((flags & SELECTION_WITH_SOURCE) != 0)
-                       selection_addsrc(pool, selection, flags);
-                     return SELECTION_NAME;
-                   }
-               }
-             FOR_POOL_SOLVABLES(p)     /* slow path */
-               {
-                 Solvable *s = pool->solvables + p;
-                 if (s->name == id && (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC))
-                   {
-                     if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
-                       continue;       /* just in case... src rpms can't be installed */
-                     if (pool_disabled_solvable(pool, s))
-                       continue;
-                     if ((flags & SELECTION_SOURCE_ONLY) != 0)
-                       id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
-                     queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
-                     if ((flags & SELECTION_WITH_SOURCE) != 0)
-                       selection_addsrc(pool, selection, flags);
-                     return SELECTION_NAME;
-                   }
-               }
+             Reldep *rd = GETRELDEP(pool, id);
+             id = rd->name;
            }
-         FOR_PROVIDES(p, pp, id)
+         if (pool->whatprovides[id] > 1)
+           continue;   /* we already did that one in the normal code path */
+         n = pool_id2str(pool, id);
+         if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0)
            {
-             Solvable *s = pool->solvables + p;
-             if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
-               continue;
+             queue_pushunique2(selection, SOLVER_SOLVABLE_PROVIDES, id);
              match = 1;
-             if (s->name == id && (flags & SELECTION_NAME) != 0)
-               {
-                 if ((flags & SELECTION_SOURCE_ONLY) != 0)
-                   id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
-                 queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
-                 if ((flags & SELECTION_WITH_SOURCE) != 0)
-                   selection_addsrc(pool, selection, flags);
-                 return SELECTION_NAME;
-               }
-           }
-         if (match && (flags & SELECTION_PROVIDES) != 0)
-           {
-             queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
-             return SELECTION_PROVIDES;
            }
        }
     }
+  return match;
+}
 
-  if ((flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0)
-    doglob = 1;
-
-  if (!doglob && !(flags & SELECTION_NOCASE))
-    return 0;
-
-  if (doglob && (flags & SELECTION_NOCASE) != 0)
-    globflags = FNM_CASEFOLD;
+/* this is the fast path of selection_provides: the id for the name
+ * 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)
+{
+  Id p, pp;
 
-#if 0  /* doesn't work with selection_filter_rel yet */
-  if (doglob && !strcmp(name, "*") && (flags & SELECTION_FLAT) != 0)
+  FOR_PROVIDES(p, pp, id)
     {
-      /* can't do this for SELECTION_PROVIDES, as src rpms don't provide anything */
-      if ((flags & SELECTION_NAME) != 0)
-       {
-         queue_push2(selection, SOLVER_SOLVABLE_ALL, 0);
-          return SELECTION_NAME;
-       }
+      Solvable *s = pool->solvables + p;
+      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+       continue;
+      queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
+      return SELECTION_PROVIDES;
     }
-#endif
 
-  if ((flags & SELECTION_NAME) != 0)
+  if ((flags & (SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) != 0)
     {
-      /* looks like a name glob. hard work. */
-      FOR_POOL_SOLVABLES(p)
-        {
-          Solvable *s = pool->solvables + p;
-          if (s->repo != pool->installed && !pool_installable(pool, s))
-           {
-             if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
-                continue;
-             if (pool_disabled_solvable(pool, s))
-               continue;
-           }
-         if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
-           continue;
-          id = s->name;
-          if ((doglob ? fnmatch(name, pool_id2str(pool, id), globflags) : strcasecmp(name, pool_id2str(pool, id))) == 0)
-            {
-             if ((flags & SELECTION_SOURCE_ONLY) != 0)
-               id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
-             /* queue_pushunique2 */
-              for (i = 0; i < selection->count; i += 2)
-                if (selection->elements[i] == SOLVER_SOLVABLE_NAME && selection->elements[i + 1] == id)
-                  break;
-              if (i == selection->count)
-                queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
-              match = 1;
-            }
-        }
-      if (match)
+      /* 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);         /* no extra package found */
+      else
        {
-         if ((flags & SELECTION_WITH_SOURCE) != 0)
-           selection_addsrc(pool, selection, flags);
-          return SELECTION_NAME;
+          selection->elements[0] = SOLVER_SOLVABLE_PROVIDES;
+          selection->elements[1] = id;
        }
+      return selection->count ? SELECTION_PROVIDES : 0;
     }
-  if ((flags & SELECTION_PROVIDES))
-    {
-      /* looks like a dep glob. really hard work. */
-      for (id = 1; id < pool->ss.nstrings; id++)
-        {
-          if (!pool->whatprovides[id] || pool->whatprovides[id] == 1)
-            continue;
-          if ((doglob ? fnmatch(name, pool_id2str(pool, id), globflags) : strcasecmp(name, pool_id2str(pool, id))) == 0)
-            {
-             if ((flags & SELECTION_INSTALLED_ONLY) != 0)
-               {
-                 FOR_PROVIDES(p, pp, id)
-                   if (pool->solvables[p].repo == pool->installed)
-                     break;
-                 if (!p)
-                   continue;
-               }
-             queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
-              match = 1;
-            }
-        }
-      if (match)
-        return SELECTION_PROVIDES;
-    }
+
   return 0;
 }
 
+/* match the provides of a package */
+/* note that we only return raw SOLVER_SOLVABLE_PROVIDES jobs
+ * so that the selection can be modified later. */
 static int
-selection_depglob_arch(Pool *pool, Queue *selection, const char *name, int flags)
+selection_provides(Pool *pool, Queue *selection, const char *name, int flags)
 {
-  int ret;
-  const char *r;
-  Id archid;
+  Id id, p, pp;
+  int match;
+  int doglob;
+  int nocase;
+  int globflags;
+  const char *n;
 
-  if ((ret = selection_depglob(pool, selection, name, flags)) != 0)
-    return ret;
-  if (!(flags & SELECTION_DOTARCH))
-    return 0;
-  /* check if there is an .arch suffix */
-  if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
+  if ((flags & SELECTION_SOURCE_ONLY) != 0)
+    return 0;  /* sources do not have provides */
+
+  nocase = flags & SELECTION_NOCASE;
+  if (!nocase)
     {
-      char *rname = solv_strdup(name);
-      rname[r - name] = 0;
-      if (archid == ARCH_SRC || archid == ARCH_NOSRC)
-       flags |= SELECTION_SOURCE_ONLY;
-      if ((ret = selection_depglob(pool, selection, rname, flags)) != 0)
+      /* try the fast path first */
+      id = pool_str2id(pool, name, 0);
+      if (id)
        {
-         selection_filter_rel(pool, selection, REL_ARCH, archid);
-         solv_free(rname);
-         return ret | SELECTION_DOTARCH;
+         /* the id is known, do the fast id matching */
+         int ret = selection_provides_id(pool, selection, id, flags);
+         if (ret)
+           return ret;
        }
-      solv_free(rname);
     }
-  return 0;
-}
 
-static int
-selection_filelist(Pool *pool, Queue *selection, const char *name, int flags)
-{
-  Dataiterator di;
-  Queue q;
-  int type;
+  doglob = (flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0;
+  if (!nocase && !doglob)
+    {
+      /* all done above in selection_provides_id */
+      return 0;
+    }
 
-  type = !(flags & SELECTION_GLOB) || strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB;
-  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);
-  while (dataiterator_step(&di))
+  /* looks like a glob or nocase match. really hard work. */
+  match = 0;
+  globflags = doglob && nocase ? FNM_CASEFOLD : 0;
+  for (id = 1; id < pool->ss.nstrings; id++)
     {
-      Solvable *s = pool->solvables + di.solvid;
-      if (!s->repo)
+      /* do we habe packages providing this id? */
+      if ((!pool->whatprovides[id] && pool->addedfileprovides == 2) || pool->whatprovides[id] == 1)
        continue;
-      if (s->repo != pool->installed && !pool_installable(pool, s))
+      n = pool_id2str(pool, id);
+      if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0)
        {
-         if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
-           continue;
-         if (pool_disabled_solvable(pool, s))
-           continue;
+         if ((flags & SELECTION_INSTALLED_ONLY) != 0)
+           {
+             FOR_PROVIDES(p, pp, id)
+               if (pool->solvables[p].repo == pool->installed)
+                 break;
+             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;
        }
-      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
-       continue;
-      queue_push(&q, di.solvid);
-      dataiterator_skip_solvable(&di);
     }
-  dataiterator_free(&di);
-  if (!q.count)
-    return 0;
-  if (q.count > 1) 
-    queue_push2(selection, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
-  else
-    queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]);
-  queue_free(&q);
-  return SELECTION_FILELIST;
-}
 
+  if (flags & (SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED))
+    match |= selection_addextra_provides(pool, selection, name, flags);
+
+  return match ? SELECTION_PROVIDES : 0;
+}
+
+/*****  name matching  *****/
+
+/* this is the fast path of selection_name: the id for the name
+ * is known and thus we can quickly check the existance of a
+ * package with that name */
 static int
-selection_rel(Pool *pool, Queue *selection, const char *name, int flags)
-{
-  int ret, rflags = 0;
-  char *r, *rname;
-  
-  /* relation case, support:
-   * depglob rel
-   * depglob.arch rel
-   */
-  rname = solv_strdup(name);
-  if ((r = strpbrk(rname, "<=>")) != 0)
-    {
-      int nend = r - rname;
-      if (nend && *r == '=' && r[-1] == '!')
-       {
-         nend--;
-         r++;
-         rflags = REL_LT|REL_GT;
-       }
-      for (; *r; r++)
-        {
-          if (*r == '<')
-            rflags |= REL_LT;
-          else if (*r == '=')
-            rflags |= REL_EQ;
-          else if (*r == '>')
-            rflags |= REL_GT;
-          else
-            break;
-        }
-      while (*r && (*r == ' ' || *r == '\t'))
-        r++;
-      while (nend && (rname[nend - 1] == ' ' || rname[nend -1 ] == '\t'))
-        nend--;
-      if (!*rname || !*r)
-        {
-         solv_free(rname);
-         return 0;
-        }
-      rname[nend] = 0;
+selection_name_id(Pool *pool, Queue *selection, Id id, int flags)
+{
+  Id p, pp, matchid;
+
+  matchid = id;
+  if ((flags & SELECTION_SOURCE_ONLY) != 0)
+    {
+      /* sources cannot be installed */
+      if ((flags & SELECTION_INSTALLED_ONLY) != 0)
+       return 0;
+      /* add ARCH_SRC to match only sources */
+      matchid = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
+      flags &= ~SELECTION_WITH_SOURCE;
+    }
+
+  FOR_PROVIDES(p, pp, matchid)
+    {
+      Solvable *s = pool->solvables + p;
+      if (s->name != id)
+       continue;
+      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+       continue;
+      /* one match is all we need */
+      queue_push2(selection, SOLVER_SOLVABLE_NAME, matchid);
+      /* add the requested extra packages */
+      if ((flags & (SELECTION_WITH_SOURCE | SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) != 0)
+       selection_addextra(pool, selection, flags);
+      return SELECTION_NAME;
+    }
+
+  if ((flags & (SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) != 0)
+    {
+      queue_push2(selection, SOLVER_SOLVABLE_NAME, matchid);
+      selection_addextra(pool, selection, flags);
+      if (selection->elements[0] == SOLVER_SOLVABLE_NAME)
+       queue_empty(selection);
+      return selection->count ? SELECTION_NAME : 0;
+    }
+
+  if ((flags & SELECTION_WITH_SOURCE) != 0 && (flags & SELECTION_INSTALLED_ONLY) == 0)
+    {
+      /* WITH_SOURCE case, but we had no match. try SOURCE_ONLY instead */
+      matchid = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
+      FOR_PROVIDES(p, pp, matchid)
+       {
+         Solvable *s = pool->solvables + p;
+         if (s->name != id)
+           continue;
+         queue_push2(selection, SOLVER_SOLVABLE_NAME, matchid);
+         return SELECTION_NAME;
+       }
+    }
+  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
+selection_name(Pool *pool, Queue *selection, const char *name, int flags)
+{
+  Id id, p;
+  int match;
+  int doglob, nocase;
+  int globflags;
+  const char *n;
+
+  if ((flags & SELECTION_SOURCE_ONLY) != 0)
+    flags &= ~SELECTION_WITH_SOURCE;
+
+  nocase = flags & SELECTION_NOCASE;
+  if (!nocase && !(flags & SELECTION_SKIP_KIND))
+    {
+      /* try the fast path first */
+      id = pool_str2id(pool, name, 0);
+      if (id)
+       {
+         int ret = selection_name_id(pool, selection, id, flags);
+         if (ret)
+           return ret;
+       }
+    }
+
+  doglob = (flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0;
+  if (!nocase && !(flags & SELECTION_SKIP_KIND) && !doglob)
+    return 0;  /* all done above in selection_name_id */
+
+  /* do a name match over all packages. hard work. */
+  match = 0;
+  globflags = doglob && nocase ? FNM_CASEFOLD : 0;
+  FOR_POOL_SOLVABLES(p)
+    {
+      Solvable *s = pool->solvables + p;
+      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+       continue;
+      if (!solvable_matches_selection_flags(pool, s, flags))
+       continue;
+      id = s->name;
+      n = pool_id2str(pool, id);
+      if (flags & SELECTION_SKIP_KIND)
+       n = skipkind(n);
+      if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0)
+       {
+         if ((flags & SELECTION_SOURCE_ONLY) != 0)
+           {
+             if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+               continue;
+             id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
+           }
+         queue_pushunique2(selection, SOLVER_SOLVABLE_NAME, id);
+         match = 1;
+       }
+    }
+  if (match)
+    {
+      /* if there was a match widen the selector to include all extra packages */
+      if ((flags & (SELECTION_WITH_SOURCE | SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) != 0)
+       selection_addextra(pool, selection, flags);
+      return SELECTION_NAME;
     }
-  if ((ret = selection_depglob_arch(pool, selection, rname, flags)) != 0)
+  return 0;
+}
+
+
+/*****  SELECTION_DOTARCH and SELECTION_REL handling *****/
+
+/* like selection_name, but check for a .arch suffix if the match did
+   not work and SELECTION_DOTARCH is used */
+static int
+selection_name_arch(Pool *pool, Queue *selection, const char *name, int flags, int doprovides, int noprune)
+{
+  int ret;
+  const char *r;
+  Id archid;
+
+  if (doprovides)
+    ret = selection_provides(pool, selection, name, flags);
+  else
+    ret = selection_name(pool, selection, name, flags);
+  if (ret)
+    return ret;
+  if (!(flags & SELECTION_DOTARCH))
+    return 0;
+  /* check if there is an .arch suffix */
+  if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
     {
-      if (rflags)
-       selection_filter_rel(pool, selection, rflags, pool_str2id(pool, r, 1));
+      char *rname = solv_strdup(name);
+      rname[r - name] = 0;
+      if (archid == ARCH_SRC || archid == ARCH_NOSRC)
+       flags |= SELECTION_SOURCE_ONLY;
+      if (doprovides)
+       ret = selection_provides(pool, selection, rname, flags);
+      else
+       ret = selection_name(pool, selection, rname, flags);
+      if (ret)
+       {
+         selection_filter_rel_noprune(pool, selection, REL_ARCH, archid);
+         if (!noprune)
+           selection_prune(pool, selection);
+       }
       solv_free(rname);
-      return ret | SELECTION_REL;
+      return ret && selection->count ? ret | SELECTION_DOTARCH : 0;
     }
-  solv_free(rname);
   return 0;
 }
 
+static char *
+splitrel(char *rname, char *r, int *rflagsp)
+{
+  int nend = r - rname;
+  int rflags = 0;
+  if (nend && *r == '=' && r[-1] == '!')
+    {
+      nend--;
+      r++;
+      rflags = REL_LT|REL_GT;
+    }
+  for (; *r; r++)
+    {
+      if (*r == '<')
+       rflags |= REL_LT;
+      else if (*r == '=')
+       rflags |= REL_EQ;
+      else if (*r == '>')
+       rflags |= REL_GT;
+      else
+       break;
+    }
+  while (*r && (*r == ' ' || *r == '\t'))
+    r++;
+  while (nend && (rname[nend - 1] == ' ' || rname[nend - 1] == '\t'))
+    nend--;
+  if (nend <= 0 || !*r || !rflags)
+    return 0;
+  *rflagsp = rflags;
+  rname[nend] = 0;
+  return r;
+}
+
+/* match name/provides, support DOTARCH and REL modifiers
+ */
+static int
+selection_name_arch_rel(Pool *pool, Queue *selection, const char *name, int flags, int doprovides)
+{
+  int ret, rflags = 0, noprune;
+  char *r = 0, *rname = 0;
+
+  /* try to split off an relation part */
+  if ((flags & SELECTION_REL) != 0)
+    {
+      if ((r = strpbrk(name, "<=>")) != 0)
+       {
+         rname = solv_strdup(name);
+         r = rname + (r - name);
+         if ((r = splitrel(rname, r, &rflags)) == 0)
+           rname = solv_free(rname);
+       }
+    }
+
+  /* check if we need to call selection_addextra */
+  noprune = doprovides && (flags & (SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH));
+
+  if (!r)
+    {
+      /* could not split off relation */
+      ret = selection_name_arch(pool, selection, name, flags, doprovides, noprune);
+      if (ret && noprune)
+       {
+         selection_addextra(pool, selection, flags);
+         selection_prune(pool, selection);
+       }
+      return ret && selection->count ? ret : 0;
+    }
+
+  /* we could split of a relation. prune name and then filter rel */
+  ret = selection_name_arch(pool, selection, rname, flags, doprovides, noprune);
+  if (ret)
+    {
+      selection_filter_rel_noprune(pool, selection, rflags, pool_str2id(pool, r, 1));
+      if (noprune)
+        selection_addextra(pool, selection, flags);
+      selection_prune(pool, selection);
+    }
+  solv_free(rname);
+  return ret && selection->count ? ret | SELECTION_REL : 0;
+}
+
+/*****  filelist matching  *****/
+
+static int
+selection_filelist_sortcmp(const void *ap, const void *bp, void *dp)
+{
+  Pool *pool = dp;
+  const Id *a = ap, *b = bp;
+  if (a[0] != b[0])
+    return strcmp(pool_id2str(pool, a[0]), pool_id2str(pool, b[0]));
+  return a[1] - b[1];
+}
+
+static int
+selection_filelist(Pool *pool, Queue *selection, const char *name, int flags)
+{
+  Dataiterator di;
+  Queue q;
+  Id id;
+  int type;
+  int i, j, lastid;
+
+  /* all files in the file list start with a '/' */
+  if (*name != '/')
+    {
+      if (!(flags & SELECTION_GLOB))
+       return 0;
+      if (*name != '*' && *name != '[' && *name != '?')
+       return 0;
+    }
+  type = !(flags & SELECTION_GLOB) || strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB;
+  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);
+  while (dataiterator_step(&di))
+    {
+      Solvable *s = pool->solvables + di.solvid;
+      if (!s->repo)
+       continue;
+      if (!solvable_matches_selection_flags(pool, s, flags))
+       continue;
+      if ((flags & SELECTION_FLAT) != 0)
+       {
+         /* don't bother with the complex stuff */
+          queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, di.solvid);
+         dataiterator_skip_solvable(&di);
+         continue;
+        }
+      id = pool_str2id(pool, di.kv.str, 1);
+      queue_push2(&q, id, di.solvid);
+    }
+  dataiterator_free(&di);
+  if ((flags & SELECTION_FLAT) != 0)
+    {
+      queue_free(&q);
+      return selection->count ? SELECTION_FILELIST : 0;
+    }
+  if (!q.count)
+    {
+      queue_free(&q);
+      return 0;
+    }
+  solv_sort(q.elements, q.count / 2, 2 * sizeof(Id), selection_filelist_sortcmp, pool);
+  lastid = 0;
+  queue_push2(&q, 0, 0);
+  for (i = j = 0; i < q.count; i += 2)
+    {
+      if (q.elements[i] != lastid)
+       {
+         if (j == 1)
+           queue_pushunique2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]);
+         else if (j > 1)
+           {
+             int k;
+             Id *idp;
+             /* check if we already have it */
+             for (k = 0; k < selection->count; k += 2)
+               {
+                 if (selection->elements[k] != SOLVER_SOLVABLE_ONE_OF)
+                   continue;
+                 idp = pool->whatprovidesdata + selection->elements[k + 1];
+                 if (!memcmp(idp, q.elements, j * sizeof(Id)) && !idp[j])
+                   break;
+               }
+             if (k == selection->count)
+               queue_push2(selection, SOLVER_SOLVABLE_ONE_OF, pool_ids2whatprovides(pool, q.elements, j));
+           }
+          lastid = q.elements[i];
+          j = 0;
+       }
+      if (!j || q.elements[j - 1] != q.elements[i])
+        q.elements[j++] = q.elements[i + 1];
+    }
+  queue_free(&q);
+  return SELECTION_FILELIST;
+}
+
+
+/*****  canon name matching  *****/
+
 #if defined(MULTI_SEMANTICS)
 # define EVRCMP_DEPCMP (pool->disttype == DISTTYPE_DEB ? EVRCMP_COMPARE : EVRCMP_MATCH_RELEASE)
 #elif defined(DEBIAN)
@@ -575,11 +1049,22 @@ selection_rel(Pool *pool, Queue *selection, const char *name, int flags)
 
 /* magic epoch promotion code, works only for SELECTION_NAME selections */
 static void
-selection_filter_evr(Pool *pool, Queue *selection, char *evr)
+selection_filter_evr(Pool *pool, Queue *selection, const char *evr)
 {
   int i, j;
   Queue q;
   Id qbuf[10];
+  const char *sp;
+
+  /* do we already have an epoch? */
+  for (sp = evr; *sp >= '0' && *sp <= '9'; sp++)
+    ;
+  if (*sp == ':' && sp != evr)
+    {
+      /* yes, just add the rel filter */
+      selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, evr, 1));
+      return;
+    }
 
   queue_init(&q);
   queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
@@ -596,7 +1081,6 @@ selection_filter_evr(Pool *pool, Queue *selection, char *evr)
        {
          Solvable *s = pool->solvables + p;
          const char *sevr = pool_id2str(pool, s->evr);
-         const char *sp;
          for (sp = sevr; *sp >= '0' && *sp <= '9'; sp++)
            ;
          if (*sp != ':')
@@ -671,6 +1155,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)
@@ -678,7 +1175,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
       rname = solv_strdup(name);       /* so we can modify it */
       r = rname + (r - name);
       *r++ = 0;
-      if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
+      if ((ret = selection_name(pool, selection, rname, flags)) == 0)
        {
          solv_free(rname);
          return 0;
@@ -691,7 +1188,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
        }
       selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1));
       solv_free(rname);
-      return ret | SELECTION_CANON;
+      return selection->count ? ret | SELECTION_CANON : 0;
     }
 
   if (pool->disttype == DISTTYPE_HAIKU)
@@ -701,7 +1198,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
       rname = solv_strdup(name);       /* so we can modify it */
       r = rname + (r - name);
       *r++ = 0;
-      if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
+      if ((ret = selection_name(pool, selection, rname, flags)) == 0)
        {
          solv_free(rname);
          return 0;
@@ -714,14 +1211,14 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
        }
       selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1));
       solv_free(rname);
-      return ret | SELECTION_CANON;
+      return selection->count ? ret | SELECTION_CANON : 0;
     }
 
   if ((r = strrchr(name, '-')) == 0)
     return 0;
   rname = solv_strdup(name);   /* so we can modify it */
   r = rname + (r - name);
-  *r = 0; 
+  *r = 0;
 
   /* split off potential arch part from version */
   if ((r2 = strrchr(r + 1, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
@@ -730,7 +1227,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
     flags |= SELECTION_SOURCE_ONLY;
 
   /* try with just the version */
-  if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
+  if ((ret = selection_name(pool, selection, rname, flags)) == 0)
     {
       /* no luck, try with version-release */
       if ((r2 = strrchr(rname, '-')) == 0)
@@ -738,10 +1235,10 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
          solv_free(rname);
          return 0;
        }
-      *r = '-'; 
-      *r2 = 0; 
+      *r = '-';
+      *r2 = 0;
       r = r2;
-      if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
+      if ((ret = selection_name(pool, selection, rname, flags)) == 0)
        {
          solv_free(rname);
          return 0;
@@ -751,90 +1248,610 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
     selection_filter_rel(pool, selection, REL_ARCH, archid);
   selection_filter_evr(pool, selection, r + 1);        /* magic epoch promotion */
   solv_free(rname);
-  return ret | SELECTION_CANON;
+  return selection->count ? ret | SELECTION_CANON : 0;
+}
+
+/* return the needed withbits to match the provided selection */
+static int
+selection_extrabits(Pool *pool, Queue *selection, int flags)
+{
+  int i, needflags, isextra;
+  int allflags;
+  Id p;
+  Solvable *s;
+  Queue qlimit;
+
+  allflags = flags & (SELECTION_WITH_SOURCE | SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH);
+  if (!selection->count)
+    return allflags;
+  if (selection->count == 2 && selection->elements[0] == SOLVER_SOLVABLE_ALL)
+    return allflags;   /* don't bother */
+  queue_init(&qlimit);
+  selection_solvables(pool, selection, &qlimit);
+  needflags = 0;
+  for (i = 0; i < qlimit.count; i++)
+    {
+      p = qlimit.elements[i];
+      s = pool->solvables + p;
+      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+       continue;
+      isextra = 0;
+      if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+       {
+         if (!(flags & SELECTION_WITH_SOURCE))
+           continue;
+          isextra |= SELECTION_WITH_SOURCE;
+         if (pool_disabled_solvable(pool, s))
+           {
+             if (!(flags & SELECTION_WITH_DISABLED))
+               continue;
+             isextra |= SELECTION_WITH_DISABLED;
+           }
+       }
+      else
+       {
+         if ((flags & SELECTION_SOURCE_ONLY) != 0)
+           continue;
+         if (s->repo != pool->installed)
+           {
+             if (pool_disabled_solvable(pool, s))
+               {
+                 if (!(flags & SELECTION_WITH_DISABLED))
+                   continue;
+                 isextra |= SELECTION_WITH_DISABLED;
+               }
+             if (pool_badarch_solvable(pool, s))
+               {
+                 if (!(flags & SELECTION_WITH_BADARCH))
+                   continue;
+                 isextra |= SELECTION_WITH_BADARCH;
+               }
+           }
+       }
+      if (isextra)
+       {
+         needflags |= isextra;
+         if (needflags == allflags)
+           break;
+       }
+    }
+  queue_free(&qlimit);
+  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;
-  const char *r;
+  if ((flags & SELECTION_MODEBITS) != SELECTION_REPLACE)
+    {
+      Queue q;
 
+      if ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT || (flags & SELECTION_MODEBITS) == SELECTION_FILTER)
+       {
+         if (!selection->count)
+           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);
+      return selection_combine(pool, selection, &q, flags, ret);
+    }
   queue_empty(selection);
-  if (*name == '/' && (flags & SELECTION_FILELIST))
+  if ((flags & SELECTION_INSTALLED_ONLY) != 0 && !pool->installed)
+    return 0;
+
+  /* here come our four selection modes */
+  if ((flags & SELECTION_FILELIST) != 0)
     ret = selection_filelist(pool, selection, name, flags);
-  if (!ret && (flags & SELECTION_REL) != 0 && (r = strpbrk(name, "<=>")) != 0)
-    ret = selection_rel(pool, selection, name, flags);
-  if (!ret)
-    ret = selection_depglob_arch(pool, selection, name, flags);
+  if (!ret && (flags & SELECTION_NAME) != 0)
+    ret = selection_name_arch_rel(pool, selection, name, flags, 0);
+  if (!ret && (flags & SELECTION_PROVIDES) != 0)
+    ret = selection_name_arch_rel(pool, selection, name, flags, 1);
   if (!ret && (flags & SELECTION_CANON) != 0)
     ret = selection_canon(pool, selection, name, flags);
-  if (ret && !selection->count)
-    ret = 0;   /* no match -> always return zero */
+
+  /* now do result filtering */
+  if (ret && (flags & SELECTION_INSTALLED_ONLY) != 0)
+    selection_filter_repo(pool, selection, pool->installed, SOLVER_SETREPO);
+
+  /* flatten if requested */
   if (ret && (flags & SELECTION_FLAT) != 0)
     selection_flatten(pool, selection);
-  return ret;
+  return selection->count ? ret : 0;
 }
 
-void
-selection_filter(Pool *pool, Queue *sel1, Queue *sel2)
+static int
+matchdep_str(const char *pattern, const char *string, int flags)
 {
-  int i, j, miss;
-  Id p, pp;
-  Queue q1;
-  Map m2;
-  Id setflags = 0;
+  if (!pattern || !string)
+    return 0;
+  if (flags & SELECTION_GLOB)
+    {
+      int globflags = (flags & SELECTION_NOCASE) != 0 ? FNM_CASEFOLD : 0;
+      return fnmatch(pattern, string, globflags) == 0 ? 1 : 0;
+    }
+  if (flags & SELECTION_NOCASE)
+    return strcasecmp(pattern, string) == 0 ? 1 : 0;
+  return strcmp(pattern, string) == 0 ? 1 : 0;
+}
 
-  if (!sel1->count || !sel2->count)
+/* like pool_match_dep but uses matchdep_str to match the name for glob and nocase matching */
+static int
+matchdep(Pool *pool, Id id, char *rname, int rflags, Id revr, int flags)
+{
+  if (ISRELDEP(id))
     {
-      queue_empty(sel1);
-      return;
+      Reldep *rd = GETRELDEP(pool, id);
+      if (rd->flags > 7)
+       {
+         if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_WITH || rd->flags == REL_WITHOUT || rd->flags == REL_COND || rd->flags == REL_UNLESS)
+           {
+             if (matchdep(pool, rd->name, rname, rflags, revr, flags))
+               return 1;
+             if ((rd->flags == REL_COND || rd->flags == REL_UNLESS) && ISRELDEP(rd->evr))
+               {
+                 rd = GETRELDEP(pool, rd->evr);
+                 if (rd->flags != REL_ELSE)
+                   return 0;
+               }
+             if (rd->flags != REL_COND && rd->flags != REL_UNLESS && rd->flags != REL_WITHOUT && matchdep(pool, rd->evr, rname, rflags, revr, flags))
+               return 1;
+             return 0;
+           }
+         if (rd->flags == REL_ARCH)
+           return matchdep(pool, rd->name, rname, rflags, revr, flags);
+       }
+      if (!matchdep(pool, rd->name, rname, rflags, revr, flags))
+       return 0;
+      if (rflags && !pool_intersect_evrs(pool, rd->flags, rd->evr, rflags, revr))
+       return 0;
+      return 1;
     }
-  if (sel1->count == 2 && (sel1->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL)
+  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)
     {
-      /* XXX: not 100% correct, but very useful */
-      queue_free(sel1);
-      queue_init_clone(sel1, sel2);
-      return;
+      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_init(&q1);
-  map_init(&m2, pool->nsolvables);
-  for (i = 0; i < sel2->count; i += 2)
+
+  queue_empty(selection);
+  if (!limiter->end)
+    return 0;
+  if (!solvidq && !solvid)
+    return 0;
+  if (solvidq && solvid)
+    return 0;
+
+  if (solvidq)
     {
-      Id select = sel2->elements[i] & SOLVER_SELECTMASK;
-      if (select == SOLVER_SOLVABLE_ALL)
+      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(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;
+  char *rname = 0, *r = 0;
+  int rflags = 0;
+  Id revr = 0;
+  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;
+
+  if (name)
+    {
+      rname = solv_strdup(name);
+      if ((flags & SELECTION_REL) != 0)
        {
-         queue_free(&q1);
-         map_free(&m2);
-         return;
+         if ((r = strpbrk(rname, "<=>")) != 0)
+           {
+             if ((r = splitrel(rname, r, &rflags)) == 0)
+               {
+                 solv_free(rname);
+                 return 0;
+               }
+           }
+         revr = pool_str2id(pool, r, 1);
+         ret |= SELECTION_REL;
        }
-      if (select == SOLVER_SOLVABLE_REPO)
+      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)
        {
-         Solvable *s;
-         Repo *repo = pool_id2repo(pool, sel2->elements[i + 1]);
-         if (repo)
-           FOR_REPO_SOLVABLES(repo, p, s)
-             map_set(&m2, p);
+         /* we can use the faster selection_make_matchdepid */
+         dep = pool_str2id(pool, rname, 1);
+         if (rflags)
+           dep = pool_rel2id(pool, dep, revr, rflags, 1);
+         rname = solv_free(rname);
+         name = 0;
+       }
+    }
+  if (dep)
+    {
+      if (keyname == SOLVABLE_NAME && (flags & SELECTION_MATCH_DEPSTR) != 0)
+       {
+         Reldep *rd;
+         if (!ISRELDEP(dep))
+           return 0;
+         rd = GETRELDEP(pool, dep);
+         if (!rd->name || rd->flags != REL_EQ)
+           return 0;
+         dep = rd->name;
+         rflags = rd->flags;
+         revr = rd->evr;
+       }
+    }
+
+  queue_init(&q);
+  for (li = limiter->start; li < limiter->end; li++)
+    {
+      Solvable *s;
+      p = limiter->mapper ? limiter->mapper[li] : li;
+      s = pool->solvables + p;
+      if (!s->repo || (limiter->repofilter && s->repo != limiter->repofilter))
+       continue;
+      if (!solvable_matches_selection_flags(pool, s, flags))
+       continue;
+      if (keyname == SOLVABLE_NAME)                    /* nevr match hack */
+       {
+         if (dep)
+           {
+             if ((flags & SELECTION_MATCH_DEPSTR) != 0)
+               {
+                 if (s->name != dep || s->evr != revr)
+                   continue;
+               }
+             else
+               {
+                 if (!pool_match_nevr(pool, s, dep))
+                   continue;
+               }
+           }
+         else
+           {
+             if ((flags & SELECTION_MATCH_DEPSTR) != 0)        /* mis-use */
+               {
+                 char *tmp = pool_tmpjoin(pool, pool_id2str(pool, s->name), " = ", pool_id2str(pool, s->evr));
+                 if (!matchdep_str(rname, tmp, flags))
+                   continue;
+               }
+             else
+               {
+                 if (!matchdep(pool, s->name, rname, rflags, revr, flags))
+                   continue;
+                 if (rflags && !pool_intersect_evrs(pool, rflags, revr, REL_EQ, s->evr))
+                   continue;
+               }
+           }
+         queue_push(selection, p);
+         continue;
+       }
+      if (q.count)
+        queue_empty(&q);
+      repo_lookup_deparray(s->repo, p, keyname, &q, marker);
+      if (!q.count)
+       continue;
+      if (dep)
+       {
+         if ((flags & SELECTION_MATCH_DEPSTR) != 0)    /* mis-use */
+           {
+             for (i = 0; i < q.count; i++)
+               if (q.elements[i] == dep)
+                 break;
+           }
+         else
+           {
+             for (i = 0; i < q.count; i++)
+               if (pool_match_dep(pool, q.elements[i], dep))
+                 break;
+           }
        }
       else
        {
-         FOR_JOB_SELECT(p, pp, select, sel2->elements[i + 1])
-           map_set(&m2, p);
+         if ((flags & SELECTION_MATCH_DEPSTR) != 0)
+           {
+             for (i = 0; i < q.count; i++)
+               if (matchdep_str(rname, pool_dep2str(pool, q.elements[i]), flags))
+                 break;
+           }
+         else
+           {
+             for (i = 0; i < q.count; i++)
+               if (matchdep(pool, q.elements[i], rname, rflags, revr, flags))
+                 break;
+           }
        }
+      if (i < q.count)
+       queue_push(selection, p);
+    }
+  queue_free(&q);
+  solv_free(rname);
+
+  /* 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 ret | (keyname == SOLVABLE_NAME ? SELECTION_NAME : SELECTION_PROVIDES);
+}
+
+static void
+setup_limiter(Pool *pool, Queue *selection, 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;
+    }
+  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;
+    }
+  /* 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);
+}
+
+/*
+ *  select against the dependencies in keyname
+ *  like SELECTION_PROVIDES, but with the deps in keyname instead of provides.
+ *  supported match modifiers:
+ *    SELECTION_REL
+ *    SELECTION_GLOB
+ *    SELECTION_NOCASE
+ */
+int
+selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int flags, int keyname, int 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;
+}
+
+/*
+ *  select against the dependency id in keyname
+ */
+int
+selection_make_matchdepid(Pool *pool, Queue *selection, Id dep, int flags, int keyname, int 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
+pool_is_kind(Pool *pool, Id name, Id kind)
+{
+  const char *n;
+  if (!kind)
+    return 1;
+  n = pool_id2str(pool, name);
+  if (kind != 1)
+    {
+      const char *kn = pool_id2str(pool, kind);
+      int knl = strlen(kn);
+      return !strncmp(n, kn, knl) && n[knl] == ':' ? 1 : 0;
     }
-  if (sel2->count == 2)                /* XXX: AND all setmasks instead? */
-    setflags = sel2->elements[0] & SOLVER_SETMASK & ~SOLVER_NOAUTOSET;
-  for (i = j = 0; i < sel1->count; i += 2)
+  else
     {
-      Id select = sel1->elements[i] & SOLVER_SELECTMASK;
-      queue_empty(&q1);
+      if (*n == ':')
+        return 1;
+      while(*n >= 'a' && *n <= 'z')
+        n++;
+      return *n == ':' ? 0 : 1;
+    }
+}
+
+static void
+selection_filter_map(Pool *pool, Queue *sel, Map *m, int setflags)
+{
+  int i, j, miss;
+  Queue q;
+  Id p, pp;
+
+  queue_init(&q);
+  for (i = j = 0; i < sel->count; i += 2)
+    {
+      Id select = sel->elements[i] & SOLVER_SELECTMASK;
+      Id id = sel->elements[i + 1];
+      if (q.count)
+        queue_empty(&q);
       miss = 0;
       if (select == SOLVER_SOLVABLE_ALL)
        {
          FOR_POOL_SOLVABLES(p)
            {
-             if (map_tst(&m2, p))
-               queue_push(&q1, p);
+             if (map_tst(m, p))
+               queue_push(&q, p);
              else
                miss = 1;
            }
@@ -842,56 +1859,202 @@ selection_filter(Pool *pool, Queue *sel1, Queue *sel2)
       else if (select == SOLVER_SOLVABLE_REPO)
        {
          Solvable *s;
-         Repo *repo = pool_id2repo(pool, sel1->elements[i + 1]);
+         Repo *repo = pool_id2repo(pool, id);
          if (repo)
-           FOR_REPO_SOLVABLES(repo, p, s)
-             {
-               if (map_tst(&m2, p))
-                 queue_push(&q1, p);
-               else
-                 miss = 1;
-             }
+           {
+             FOR_REPO_SOLVABLES(repo, p, s)
+               {
+                 if (map_tst(m, p))
+                   queue_push(&q, p);
+                 else
+                   miss = 1;
+               }
+           }
+       }
+      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, sel1->elements[i + 1])
+         FOR_JOB_SELECT(p, pp, select, id)
            {
-             if (map_tst(&m2, p))
-               queue_pushunique(&q1, p);
+             if (map_tst(m, p))
+               queue_pushunique(&q, p);
              else
                miss = 1;
            }
        }
-      if (!q1.count)
+      if (!q.count)
        continue;
       if (!miss)
        {
-         sel1->elements[j] = sel1->elements[i] | setflags;
-         sel1->elements[j + 1] = sel1->elements[i + 1];
+         sel->elements[j] = sel->elements[i] | setflags;
+         sel->elements[j + 1] = id;
        }
-      else if (q1.count > 1) 
+      else if (q.count > 1)
        {
-         sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF | setflags;
-         sel1->elements[j + 1] = pool_queuetowhatprovides(pool, &q1);
+         sel->elements[j] = (sel->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF | setflags;
+         sel->elements[j + 1] = pool_queuetowhatprovides(pool, &q);
        }
       else
        {
-         sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE | SOLVER_NOAUTOSET | setflags;
-         sel1->elements[j + 1] = q1.elements[0];
+         sel->elements[j] = (sel->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE | SOLVER_NOAUTOSET | setflags;
+         sel->elements[j + 1] = q.elements[0];
        }
       j += 2;
     }
-  queue_truncate(sel1, j);
+  queue_truncate(sel, j);
+  queue_free(&q);
+}
+
+static void
+selection_filter_int(Pool *pool, Queue *sel1, Queue *sel2, int invert)
+{
+  int i, j;
+  Id p, pp, q1filled = 0;
+  Queue q1;
+  Map m2;
+  Id setflags = 0;
+
+  /* handle special cases */
+  if (!sel1->count || !sel2->count)
+    {
+      if (invert && !sel2->count)
+       return;
+      queue_empty(sel1);
+      return;
+    }
+  if (sel1->count == 2 && (sel1->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL && !invert)
+    {
+      /* XXX: not 100% correct, but very useful */
+      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] &= 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;
+    }
+
+  /* convert sel2 into a map */
+  queue_init(&q1);
+  map_init(&m2, pool->nsolvables);
+  for (i = 0; i < sel2->count; i += 2)
+    {
+      Id select = sel2->elements[i] & SOLVER_SELECTMASK;
+      if (select == SOLVER_SOLVABLE_ALL)
+       {
+         queue_free(&q1);
+         map_free(&m2);
+         if (invert)
+           queue_empty(sel1);
+         return;
+       }
+      if (select == SOLVER_SOLVABLE_REPO)
+       {
+         Solvable *s;
+         Repo *repo = pool_id2repo(pool, sel2->elements[i + 1]);
+         if (repo)
+           {
+             FOR_REPO_SOLVABLES(repo, p, s)
+               map_set(&m2, p);
+           }
+       }
+      else
+       {
+         if ((select == SOLVER_SOLVABLE_NAME || select == SOLVER_SOLVABLE_PROVIDES) && ISRELDEP(sel2->elements[i + 1]))
+           {
+             Reldep *rd = GETRELDEP(pool, sel2->elements[i + 1]);
+             if (rd->flags == REL_ARCH && rd->name == 0)
+               {
+                 /* special arch filter */
+                 if (!q1filled++)
+                   selection_solvables(pool, sel1, &q1);
+                 for (j = 0; j < q1.count; j++)
+                   {
+                     Id p = q1.elements[j];
+                     Solvable *s = pool->solvables + p;
+                     if (s->arch == rd->evr || (rd->evr == ARCH_SRC && s->arch == ARCH_NOSRC))
+                       map_set(&m2, p);
+                   }
+                 continue;
+               }
+             else if (rd->flags == REL_KIND && rd->name == 0)
+               {
+                 /* special kind filter */
+                 if (!q1filled++)
+                   selection_solvables(pool, sel1, &q1);
+                 for (j = 0; j < q1.count; j++)
+                   {
+                     Id p = q1.elements[j];
+                     Solvable *s = pool->solvables + p;
+                     if (pool_is_kind(pool, s->name, rd->evr))
+                       map_set(&m2, p);
+                   }
+                 continue;
+               }
+           }
+         FOR_JOB_SELECT(p, pp, select, sel2->elements[i + 1])
+           map_set(&m2, p);
+       }
+    }
   queue_free(&q1);
+
+  /* now filter sel1 with the map */
+  if (invert)
+    map_invertall(&m2);
+  if (sel2->count == 2)
+    setflags = sel2->elements[0] & (SOLVER_SETMASK & ~SOLVER_NOAUTOSET);
+  selection_filter_map(pool, sel1, &m2, setflags);
   map_free(&m2);
 }
 
 void
+selection_filter(Pool *pool, Queue *sel1, Queue *sel2)
+{
+  selection_filter_int(pool, sel1, sel2, 0);
+}
+
+void
 selection_add(Pool *pool, Queue *sel1, Queue *sel2)
 {
-  int i;
-  for (i = 0; i < sel2->count; i++)
-    queue_push(sel1, sel2->elements[i]);
+  if (sel2->count)
+    queue_insertn(sel1, sel1->count, sel2->count, sel2->elements);
+}
+
+void
+selection_subtract(Pool *pool, Queue *sel1, Queue *sel2)
+{
+  selection_filter_int(pool, sel1, sel2, 1);
 }
 
 const char *