Imported Upstream version 0.6.30 00/194200/1 upstream/0.6.30
authorDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 30 Nov 2018 03:40:57 +0000 (12:40 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 30 Nov 2018 03:40:57 +0000 (12:40 +0900)
Change-Id: I534bdf20ed04de7ba12fa6f3eabbc9bc7164de6e
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
29 files changed:
CMakeLists.txt
NEWS
TODO_1.0
VERSION.cmake
examples/solv/solv.c
ext/repo_deb.c
ext/repo_rpmdb.c
ext/testcase.c
ext/testcase.h
package/libsolv.changes
src/CMakeLists.txt
src/cleandeps.c [new file with mode: 0644]
src/libsolv.ver
src/order.c
src/policy.c
src/problems.c
src/rules.c
src/solver.c
src/solver.h
src/solver_private.h
src/solver_util.c [new file with mode: 0644]
test/testcases/cleandeps/cleandeps_dup.t
test/testcases/cleandeps/cleandeps_in.t
test/testcases/cleandeps/cleandeps_up.t
test/testcases/distupgrade/dup_multiversion3.t
test/testcases/distupgrade/dup_noarchchange.t
test/testcases/distupgrade/dup_orphan3.t
test/testcases/targeted/targeted_up.t
tools/testsolv.c

index 7902065..605ab76 100644 (file)
@@ -156,9 +156,9 @@ IF (${have_system} STRGREATER xx)
 ENDIF (${have_system} STRGREATER xx)
 
 SET (ENABLE_ZLIB_COMPRESSION ON)
 ENDIF (${have_system} STRGREATER xx)
 
 SET (ENABLE_ZLIB_COMPRESSION ON)
-IF (ENABLE_ARCHREPO)
+IF (ENABLE_ARCHREPO OR ENABLE_DEBIAN)
 SET (ENABLE_LZMA_COMPRESSION ON)
 SET (ENABLE_LZMA_COMPRESSION ON)
-ENDIF (ENABLE_ARCHREPO)
+ENDIF (ENABLE_ARCHREPO OR ENABLE_DEBIAN)
 
 IF (ENABLE_RPMMD OR ENABLE_SUSEREPO OR ENABLE_APPDATA OR ENABLE_COMPS OR ENABLE_HELIXREPO OR ENABLE_MDKREPO)
 IF (WITH_LIBXML2 )
 
 IF (ENABLE_RPMMD OR ENABLE_SUSEREPO OR ENABLE_APPDATA OR ENABLE_COMPS OR ENABLE_HELIXREPO OR ENABLE_MDKREPO)
 IF (WITH_LIBXML2 )
diff --git a/NEWS b/NEWS
index 396bfdb..e3f7845 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,14 @@
 This file contains the major changes between
 libsolv versions:
 
 This file contains the major changes between
 libsolv versions:
 
+Version 0.6.30
+- new features:
+  * many fixes and extenstions for cleandeps, e.g.
+    cleandeps now works for "update all packages"
+  * support debian packages with xz compressed control.tar
+  * always create dup rules for "distupgrade" jobs
+  * use recommends also for ordering packages
+
 Version 0.6.29
 - new features:
   * support for REL_WITHOUT and REL_UNLESS dependencies
 Version 0.6.29
 - new features:
   * support for REL_WITHOUT and REL_UNLESS dependencies
index cb5055d..6ecb862 100644 (file)
--- a/TODO_1.0
+++ b/TODO_1.0
@@ -23,8 +23,6 @@
 
 - write more manpages
 
 
 - write more manpages
 
-- use dup rules all the time
-
 - bindings: selections.flags() should be a attribute and not a method
 
 - rename repodata_lookup_id_uninternalized to repodata_lookup_id_voidid_uninternalized
 - bindings: selections.flags() should be a attribute and not a method
 
 - rename repodata_lookup_id_uninternalized to repodata_lookup_id_voidid_uninternalized
index f24ed63..b047bfc 100644 (file)
@@ -49,5 +49,5 @@ SET(LIBSOLVEXT_SOVERSION "0")
 
 SET(LIBSOLV_MAJOR "0")
 SET(LIBSOLV_MINOR "6")
 
 SET(LIBSOLV_MAJOR "0")
 SET(LIBSOLV_MINOR "6")
-SET(LIBSOLV_PATCH "29")
+SET(LIBSOLV_PATCH "30")
 
 
index 615c83d..5ee4297 100644 (file)
@@ -36,6 +36,7 @@
 #include "solver.h"
 #include "solverdebug.h"
 #include "transaction.h"
 #include "solver.h"
 #include "solverdebug.h"
 #include "transaction.h"
+#include "testcase.h"
 #ifdef SUSE
 #include "repo_autopattern.h"
 #endif
 #ifdef SUSE
 #include "repo_autopattern.h"
 #endif
@@ -228,6 +229,7 @@ main(int argc, char **argv)
   int keyname_depstr = 0;
   int debuglevel = 0;
   int answer, acnt = 0;
   int keyname_depstr = 0;
   int debuglevel = 0;
   int answer, acnt = 0;
+  char *testcase = 0;
 
   argc--;
   argv++;
 
   argc--;
   argv++;
@@ -326,6 +328,12 @@ main(int argc, char **argv)
          argc -= 2;
          argv += 2;
        }
          argc -= 2;
          argv += 2;
        }
+      else if (argc > 2 && !strcmp(argv[1], "--testcase"))
+       {
+         testcase = argv[2];
+         argc -= 2;
+         argv += 2;
+       }
       else
        break;
     }
       else
        break;
     }
@@ -679,7 +687,15 @@ rerunsolver:
       Id problem, solution;
       int pcnt, scnt;
 
       Id problem, solution;
       int pcnt, scnt;
 
-      if (!solver_solve(solv, &job))
+      pcnt = solver_solve(solv, &job);
+      if (testcase)
+       {
+         printf("Writing solver testcase:\n");
+         if (!testcase_write(solv, testcase, TESTCASE_RESULT_TRANSACTION | TESTCASE_RESULT_PROBLEMS, 0, 0))
+           printf("%s\n", pool_errstr(pool));
+         testcase = 0;
+       }
+      if (!pcnt)
        break;
       pcnt = solver_problem_count(solv);
       printf("Found %d problems:\n", pcnt);
        break;
       pcnt = solver_problem_count(solv);
       printf("Found %d problems:\n", pcnt);
index 812f3d9..277e65a 100644 (file)
@@ -12,6 +12,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <zlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <zlib.h>
+#include <lzma.h>
 #include <errno.h>
 
 #include "pool.h"
 #include <errno.h>
 
 #include "pool.h"
 #include "repo_deb.h"
 
 static unsigned char *
 #include "repo_deb.h"
 
 static unsigned char *
-decompress(unsigned char *in, int inl, int *outlp)
+decompress_gz(unsigned char *in, int inl, int *outlp, int maxoutl)
 {
   z_stream strm;
   int outl, ret;
 {
   z_stream strm;
   int outl, ret;
-  unsigned char *out;
+  unsigned char *bp, *out;
+
+  /* first skip the gz header */
+  if (inl <= 10 || in[0] != 0x1f || in[1] != 0x8b)
+    return 0;
+  if (in[2] != 8 || (in[3] & 0xe0) != 0)
+    return 0;
+  bp = in + 4;
+  bp += 6;     /* skip time, xflags and OS code */
+  if (in[3] & 0x04)
+    {
+      /* skip extra field */
+      int l = bp + 2 >= in + inl ? 0 : (bp[0] | bp[1] << 8);
+      bp += l + 2;
+    }
+  if (in[3] & 0x08)    /* orig filename */
+    while (bp < in + inl && *bp++)
+      ;
+  if (in[3] & 0x10)    /* file comment */
+    while (bp < in + inl && *bp++)
+      ;
+  if (in[3] & 0x02)    /* header crc */
+    bp += 2;
+  if (bp >= in + inl)
+    return 0;
+  inl -= bp - in;
+  in = bp;
 
   memset(&strm, 0, sizeof(strm));
   strm.next_in = in;
 
   memset(&strm, 0, sizeof(strm));
   strm.next_in = in;
@@ -46,6 +73,12 @@ decompress(unsigned char *in, int inl, int *outlp)
       if (strm.avail_out == 0)
        {
          outl += 4096;
       if (strm.avail_out == 0)
        {
          outl += 4096;
+         if (outl >= maxoutl)
+           {
+             inflateEnd(&strm);
+             free(out);
+             return 0;
+           }
          out = solv_realloc(out, outl + 4096);
          strm.next_out = out + outl;
          strm.avail_out = 4096;
          out = solv_realloc(out, outl + 4096);
          strm.next_out = out + outl;
          strm.avail_out = 4096;
@@ -55,6 +88,7 @@ decompress(unsigned char *in, int inl, int *outlp)
        break;
       if (ret != Z_OK)
        {
        break;
       if (ret != Z_OK)
        {
+         inflateEnd(&strm);
          free(out);
          return 0;
        }
          free(out);
          return 0;
        }
@@ -65,6 +99,58 @@ decompress(unsigned char *in, int inl, int *outlp)
   return out;
 }
 
   return out;
 }
 
+static unsigned char *
+decompress_xz(unsigned char *in, int inl, int *outlp, int maxoutl)
+{
+  static lzma_stream stream_init = LZMA_STREAM_INIT;
+  lzma_stream strm;
+  int outl, ret;
+  unsigned char *out;
+
+  strm = stream_init;
+  strm.next_in = in;
+  strm.avail_in = inl;
+  out = solv_malloc(4096);
+  strm.next_out = out;
+  strm.avail_out = 4096;
+  outl = 0;
+  ret = lzma_auto_decoder(&strm, 100 << 20, 0);
+  if (ret != LZMA_OK)
+    {
+      free(out);
+      return 0;
+    }
+  for (;;)
+    {
+      if (strm.avail_out == 0)
+       {
+         outl += 4096;
+         if (outl >= maxoutl)
+           {
+             lzma_end(&strm);
+             free(out);
+             return 0;
+           }
+         out = solv_realloc(out, outl + 4096);
+         strm.next_out = out + outl;
+         strm.avail_out = 4096;
+       }
+      ret = lzma_code(&strm, LZMA_RUN);
+      if (ret == LZMA_STREAM_END)
+       break;
+      if (ret != LZMA_OK)
+       {
+         lzma_end(&strm);
+         free(out);
+         return 0;
+       }
+    }
+  outl += 4096 - strm.avail_out;
+  lzma_end(&strm);
+  *outlp = outl;
+  return out;
+}
+
 static Id
 parseonedep(Pool *pool, char *p)
 {
 static Id
 parseonedep(Pool *pool, char *p)
 {
@@ -447,6 +533,10 @@ repo_add_debdb(Repo *repo, int flags)
   return 0;
 }
 
   return 0;
 }
 
+#define CONTROL_COMP_NONE      0
+#define CONTROL_COMP_GZIP      1
+#define CONTROL_COMP_XZ                2
+
 Id
 repo_add_deb(Repo *repo, const char *deb, int flags)
 {
 Id
 repo_add_deb(Repo *repo, const char *deb, int flags)
 {
@@ -454,6 +544,7 @@ repo_add_deb(Repo *repo, const char *deb, int flags)
   Repodata *data;
   unsigned char buf[4096], *bp;
   int l, l2, vlen, clen, ctarlen;
   Repodata *data;
   unsigned char buf[4096], *bp;
   int l, l2, vlen, clen, ctarlen;
+  int control_comp;
   unsigned char *ctgz;
   unsigned char pkgid[16];
   unsigned char *ctar;
   unsigned char *ctgz;
   unsigned char pkgid[16];
   unsigned char *ctar;
@@ -495,16 +586,23 @@ repo_add_deb(Repo *repo, const char *deb, int flags)
       fclose(fp);
       return 0;
     }
       fclose(fp);
       return 0;
     }
-  if (strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz  ", 16) != 0 && strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz/ ", 16) != 0)
+  control_comp = 0;
+  if (!strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz  ", 16) || !strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz/ ", 16))
+    control_comp = CONTROL_COMP_GZIP;
+  else if (!strncmp((char *)buf + 8 + 60 + vlen, "control.tar.xz  ", 16) || !strncmp((char *)buf + 8 + 60 + vlen, "control.tar.xz/ ", 16))
+    control_comp = CONTROL_COMP_XZ;
+  else if (!strncmp((char *)buf + 8 + 60 + vlen, "control.tar     ", 16) || !strncmp((char *)buf + 8 + 60 + vlen, "control.tar/    ", 16))
+    control_comp = CONTROL_COMP_NONE;
+  else
     {
     {
-      pool_error(pool, -1, "%s: control.tar.gz is not second entry", deb);
+      pool_error(pool, -1, "%s: control.tar is not second entry", deb);
       fclose(fp);
       return 0;
     }
   clen = atoi((char *)buf + 8 + 60 + vlen + 48);
   if (clen <= 0 || clen >= 0x100000)
     {
       fclose(fp);
       return 0;
     }
   clen = atoi((char *)buf + 8 + 60 + vlen + 48);
   if (clen <= 0 || clen >= 0x100000)
     {
-      pool_error(pool, -1, "%s: control.tar.gz has illegal size", deb);
+      pool_error(pool, -1, "%s: control.tar has illegal size", deb);
       fclose(fp);
       return 0;
     }
       fclose(fp);
       return 0;
     }
@@ -534,55 +632,25 @@ repo_add_deb(Repo *repo, const char *deb, int flags)
       solv_chksum_free(chk, pkgid);
       gotpkgid = 1;
     }
       solv_chksum_free(chk, pkgid);
       gotpkgid = 1;
     }
-  if (ctgz[0] != 0x1f || ctgz[1] != 0x8b)
-    {
-      pool_error(pool, -1, "%s: control.tar.gz is not gzipped", deb);
-      solv_free(ctgz);
-      return 0;
-    }
-  if (ctgz[2] != 8 || (ctgz[3] & 0xe0) != 0)
-    {
-      pool_error(pool, -1, "%s: control.tar.gz is compressed in a strange way", deb);
-      solv_free(ctgz);
-      return 0;
-    }
-  bp = ctgz + 4;
-  bp += 6;     /* skip time, xflags and OS code */
-  if (ctgz[3] & 0x04)
-    {
-      /* skip extra field */
-      l = bp[0] | bp[1] << 8;
-      bp += l + 2;
-      if (bp >= ctgz + clen)
-       {
-          pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
-         solv_free(ctgz);
-         return 0;
-       }
-    }
-  if (ctgz[3] & 0x08)  /* orig filename */
-    while (*bp)
-      bp++;
-  if (ctgz[3] & 0x10)  /* file comment */
-    while (*bp)
-      bp++;
-  if (ctgz[3] & 0x02)  /* header crc */
-    bp += 2;
-  if (bp >= ctgz + clen)
+  ctar = 0;
+  if (control_comp == CONTROL_COMP_GZIP)
+    ctar = decompress_gz(ctgz, clen, &ctarlen, 0x1000000);
+  else if (control_comp == CONTROL_COMP_XZ)
+    ctar = decompress_xz(ctgz, clen, &ctarlen, 0x1000000);
+  else
     {
     {
-      pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
-      solv_free(ctgz);
-      return 0;
+      ctarlen = clen;
+      ctar = solv_memdup(ctgz, clen);
     }
     }
-  ctar = decompress(bp, ctgz + clen - bp, &ctarlen);
   solv_free(ctgz);
   if (!ctar)
     {
   solv_free(ctgz);
   if (!ctar)
     {
-      pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
+      pool_error(pool, -1, "%s: control.tar is corrupt", deb);
       return 0;
     }
   bp = ctar;
   l = ctarlen;
       return 0;
     }
   bp = ctar;
   l = ctarlen;
+  l2 = 0;
   while (l > 512)
     {
       int j;
   while (l > 512)
     {
       int j;
@@ -590,6 +658,12 @@ repo_add_deb(Repo *repo, const char *deb, int flags)
       for (j = 124; j < 124 + 12; j++)
        if (bp[j] >= '0' && bp[j] <= '7')
          l2 = l2 * 8 + (bp[j] - '0');
       for (j = 124; j < 124 + 12; j++)
        if (bp[j] >= '0' && bp[j] <= '7')
          l2 = l2 * 8 + (bp[j] - '0');
+      if (l2 < 0 || l2 > l)
+       {
+         l2 = 0;
+         break;
+       }
+      bp[124] = 0;
       if (!strcmp((char *)bp, "./control") || !strcmp((char *)bp, "control"))
        break;
       l2 = 512 + ((l2 + 511) & ~511);
       if (!strcmp((char *)bp, "./control") || !strcmp((char *)bp, "control"))
        break;
       l2 = 512 + ((l2 + 511) & ~511);
@@ -598,7 +672,7 @@ repo_add_deb(Repo *repo, const char *deb, int flags)
     }
   if (l <= 512 || l - 512 - l2 <= 0 || l2 <= 0)
     {
     }
   if (l <= 512 || l - 512 - l2 <= 0 || l2 <= 0)
     {
-      pool_error(pool, -1, "%s: control.tar.gz contains no control file", deb);
+      pool_error(pool, -1, "%s: control.tar contains no control file", deb);
       free(ctar);
       return 0;
     }
       free(ctar);
       return 0;
     }
index 9fb5a29..ceaf351 100644 (file)
 #endif
 
 /* some limits to guard against corrupt rpms */
 #endif
 
 /* some limits to guard against corrupt rpms */
-#define MAX_SIG_CNT            0x100000
-#define MAX_SIG_DSIZE          0x100000
+/* dsize limits taken from rpm's lib/header.c */
+#define MAX_SIG_CNT            0x10000
+#define MAX_SIG_DSIZE          0x4000000
 
 
-#define MAX_HDR_CNT            0x100000
-#define MAX_HDR_DSIZE          0x2000000
+#define MAX_HDR_CNT            0x10000
+#define MAX_HDR_DSIZE          0x10000000
 
 typedef struct rpmhead {
   int cnt;
 
 typedef struct rpmhead {
   int cnt;
@@ -1190,14 +1191,15 @@ rpmdbid2db(unsigned char *db, Id id, int byteswapped)
 int
 serialize_dbenv_ops(struct rpmdbstate *state)
 {
 int
 serialize_dbenv_ops(struct rpmdbstate *state)
 {
-  char lpath[PATH_MAX];
+  char *lpath;
   mode_t oldmask;
   int fd;
   struct flock fl;
 
   mode_t oldmask;
   int fd;
   struct flock fl;
 
-  snprintf(lpath, PATH_MAX, "%s/var/lib/rpm/.dbenv.lock", state->rootdir ? state->rootdir : "");
+  lpath = solv_dupjoin(state->rootdir, "/var/lib/rpm/.dbenv.lock", 0);
   oldmask = umask(022);
   fd = open(lpath, (O_RDWR|O_CREAT), 0644);
   oldmask = umask(022);
   fd = open(lpath, (O_RDWR|O_CREAT), 0644);
+  free(lpath);
   umask(oldmask);
   if (fd < 0)
     return -1;
   umask(oldmask);
   if (fd < 0)
     return -1;
@@ -1221,7 +1223,7 @@ static int
 opendbenv(struct rpmdbstate *state)
 {
   const char *rootdir = state->rootdir;
 opendbenv(struct rpmdbstate *state)
 {
   const char *rootdir = state->rootdir;
-  char dbpath[PATH_MAX];
+  char *dbpath;
   DB_ENV *dbenv = 0;
   int r;
 
   DB_ENV *dbenv = 0;
   int r;
 
@@ -1230,13 +1232,15 @@ opendbenv(struct rpmdbstate *state)
 #if (defined(FEDORA) || defined(MAGEIA)) && (DB_VERSION_MAJOR >= 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 5))
   dbenv->set_thread_count(dbenv, 8);
 #endif
 #if (defined(FEDORA) || defined(MAGEIA)) && (DB_VERSION_MAJOR >= 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 5))
   dbenv->set_thread_count(dbenv, 8);
 #endif
-  snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm", rootdir ? rootdir : "");
+  dbpath = solv_dupjoin(rootdir, "/var/lib/rpm", 0);
   if (access(dbpath, W_OK) == -1)
     {
   if (access(dbpath, W_OK) == -1)
     {
-      snprintf(dbpath, PATH_MAX, "%s/usr/share/rpm/Packages", rootdir ? rootdir : "");
+      free(dbpath);
+      dbpath = solv_dupjoin(rootdir, "/usr/share/rpm/Packages", 0);
       if (access(dbpath, R_OK) == 0)
        state->is_ostree = 1;
       if (access(dbpath, R_OK) == 0)
        state->is_ostree = 1;
-      snprintf(dbpath, PATH_MAX, "%s%s", rootdir ? rootdir : "", state->is_ostree ? "/usr/share/rpm" : "/var/lib/rpm");
+      free(dbpath);
+      dbpath = solv_dupjoin(rootdir, state->is_ostree ? "/usr/share/rpm" : "/var/lib/rpm", 0);
       r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
     }
   else
       r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
     }
   else
@@ -1253,9 +1257,11 @@ opendbenv(struct rpmdbstate *state)
   if (r)
     {
       pool_error(state->pool, 0, "dbenv->open: %s", strerror(errno));
   if (r)
     {
       pool_error(state->pool, 0, "dbenv->open: %s", strerror(errno));
+      free(dbpath);
       dbenv->close(dbenv, 0);
       return 0;
     }
       dbenv->close(dbenv, 0);
       return 0;
     }
+  free(dbpath);
   state->dbenv = dbenv;
   return 1;
 }
   state->dbenv = dbenv;
   return 1;
 }
@@ -1286,6 +1292,22 @@ closedbenv(struct rpmdbstate *state)
   state->dbenv = 0;
 }
 
   state->dbenv = 0;
 }
 
+static int
+stat_database(struct rpmdbstate *state, char *dbname, struct stat *statbuf, int seterror)
+{
+  char *dbpath;
+  dbpath = solv_dupjoin(state->rootdir, state->is_ostree ? "/usr/share/rpm/" : "/var/lib/rpm/", dbname);
+  if (stat(dbpath, statbuf))
+    {
+      if (seterror)
+        pool_error(state->pool, -1, "%s: %s", dbpath, strerror(errno));
+      free(dbpath);
+      return -1;
+    }
+  free(dbpath);
+  return 0;
+}
+
 #endif
 
 static void
 #endif
 
 static void
@@ -1534,7 +1556,6 @@ static int
 count_headers(struct rpmdbstate *state)
 {
   Pool *pool = state->pool;
 count_headers(struct rpmdbstate *state)
 {
   Pool *pool = state->pool;
-  char dbpath[PATH_MAX];
   struct stat statbuf;
   DB *db = 0;
   DBC *dbc = 0;
   struct stat statbuf;
   DB *db = 0;
   DBC *dbc = 0;
@@ -1542,8 +1563,7 @@ count_headers(struct rpmdbstate *state)
   DBT dbkey;
   DBT dbdata;
 
   DBT dbkey;
   DBT dbdata;
 
-  snprintf(dbpath, PATH_MAX, "%s%s/Name", state->rootdir ? state->rootdir : "", state->is_ostree ? "/usr/share/rpm" : "/var/lib/rpm");
-  if (stat(dbpath, &statbuf))
+  if (stat_database(state, "Name", &statbuf, 0))
     return 0;
   memset(&dbkey, 0, sizeof(dbkey));
   memset(&dbdata, 0, sizeof(dbdata));
     return 0;
   memset(&dbkey, 0, sizeof(dbkey));
   memset(&dbdata, 0, sizeof(dbdata));
@@ -1836,7 +1856,6 @@ int
 repo_add_rpmdb(Repo *repo, Repo *ref, int flags)
 {
   Pool *pool = repo->pool;
 repo_add_rpmdb(Repo *repo, Repo *ref, int flags)
 {
   Pool *pool = repo->pool;
-  char dbpath[PATH_MAX];
   struct stat packagesstat;
   unsigned char newcookie[32];
   const unsigned char *oldcookie = 0;
   struct stat packagesstat;
   unsigned char newcookie[32];
   const unsigned char *oldcookie = 0;
@@ -1870,10 +1889,8 @@ repo_add_rpmdb(Repo *repo, Repo *ref, int flags)
     }
 
   /* XXX: should get ro lock of Packages database! */
     }
 
   /* XXX: should get ro lock of Packages database! */
-  snprintf(dbpath, PATH_MAX, "%s%s/Packages", state.rootdir ? state.rootdir : "", state.is_ostree ? "/usr/share/rpm" : "/var/lib/rpm");
-  if (stat(dbpath, &packagesstat))
+  if (stat_database(&state, "Packages", &packagesstat, 1))
     {
     {
-      pool_error(pool, -1, "%s: %s", dbpath, strerror(errno));
       freestate(&state);
       return -1;
     }
       freestate(&state);
       return -1;
     }
index 77cc719..1f19be1 100644 (file)
@@ -85,6 +85,7 @@ static struct resultflags2str {
   { TESTCASE_RESULT_RULES,             "rules" },
   { TESTCASE_RESULT_GENID,             "genid" },
   { TESTCASE_RESULT_REASON,            "reason" },
   { TESTCASE_RESULT_RULES,             "rules" },
   { TESTCASE_RESULT_GENID,             "genid" },
   { TESTCASE_RESULT_REASON,            "reason" },
+  { TESTCASE_RESULT_CLEANDEPS,         "cleandeps" },
   { 0, 0 }
 };
 
   { 0, 0 }
 };
 
@@ -925,7 +926,7 @@ str2selflags(Pool *pool, char *s)   /* modifies the string! */
            break;
          }
       if (!selflags2str[i].str)
            break;
          }
       if (!selflags2str[i].str)
-       pool_debug(pool, SOLV_ERROR, "str2job: unknown selection flag '%s'\n", s);
+       pool_error(pool, 0, "str2job: unknown selection flag '%s'", s);
       s = se;
     }
   return selflags;
       s = se;
     }
   return selflags;
@@ -947,7 +948,7 @@ str2jobflags(Pool *pool, char *s)   /* modifies the string */
            break;
          }
       if (!jobflags2str[i].str)
            break;
          }
       if (!jobflags2str[i].str)
-       pool_debug(pool, SOLV_ERROR, "str2job: unknown job flag '%s'\n", s);
+       pool_error(pool, 0, "str2job: unknown job flag '%s'", s);
       s = se;
     }
   return jobflags;
       s = se;
     }
   return jobflags;
@@ -982,7 +983,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp)
     }
   if (npieces < 3)
     {
     }
   if (npieces < 3)
     {
-      pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
+      pool_error(pool, -1, "str2job: bad line '%s'", str);
       solv_free(pieces);
       return -1;
     }
       solv_free(pieces);
       return -1;
     }
@@ -992,7 +993,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp)
       break;
   if (!job2str[i].str)
     {
       break;
   if (!job2str[i].str)
     {
-      pool_debug(pool, SOLV_ERROR, "str2job: unknown job '%s'\n", str);
+      pool_error(pool, -1, "str2job: unknown job '%s'", str);
       solv_free(pieces);
       return -1;
     }
       solv_free(pieces);
       return -1;
     }
@@ -1013,7 +1014,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp)
     {
       if (npieces != 3)
        {
     {
       if (npieces != 3)
        {
-         pool_debug(pool, SOLV_ERROR, "str2job: bad pkg selector in '%s'\n", str);
+         pool_error(pool, -1, "str2job: bad pkg selector in '%s'", str);
          solv_free(pieces);
          return -1;
        }
          solv_free(pieces);
          return -1;
        }
@@ -1021,7 +1022,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp)
       what = testcase_str2solvid(pool, pieces[2]);
       if (!what)
        {
       what = testcase_str2solvid(pool, pieces[2]);
       if (!what)
        {
-         pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[2]);
+         pool_error(pool, -1, "str2job: unknown package '%s'", pieces[2]);
          solv_free(pieces);
          return -1;
        }
          solv_free(pieces);
          return -1;
        }
@@ -1069,7 +1070,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp)
              Id p = testcase_str2solvid(pool, pieces[i]);
              if (!p)
                {
              Id p = testcase_str2solvid(pool, pieces[i]);
              if (!p)
                {
-                 pool_debug(pool, SOLV_ERROR, "str2job: unknown package '%s'\n", pieces[i]);
+                 pool_error(pool, -1, "str2job: unknown package '%s'", pieces[i]);
                  queue_free(&q);
                  solv_free(pieces);
                  return -1;
                  queue_free(&q);
                  solv_free(pieces);
                  return -1;
@@ -1085,14 +1086,14 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp)
       Repo *repo;
       if (npieces != 3)
        {
       Repo *repo;
       if (npieces != 3)
        {
-         pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
+         pool_error(pool, -1, "str2job: bad line '%s'", str);
          solv_free(pieces);
          return -1;
        }
       repo = testcase_str2repo(pool, pieces[2]);
       if (!repo)
        {
          solv_free(pieces);
          return -1;
        }
       repo = testcase_str2repo(pool, pieces[2]);
       if (!repo)
        {
-         pool_debug(pool, SOLV_ERROR, "str2job: unknown repo '%s'\n", pieces[2]);
+         pool_error(pool, -1, "str2job: unknown repo '%s'", pieces[2]);
          solv_free(pieces);
          return -1;
        }
          solv_free(pieces);
          return -1;
        }
@@ -1103,7 +1104,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp)
     {
       if (npieces != 3 && strcmp(pieces[2], "packages") != 0)
        {
     {
       if (npieces != 3 && strcmp(pieces[2], "packages") != 0)
        {
-         pool_debug(pool, SOLV_ERROR, "str2job: bad line '%s'\n", str);
+         pool_error(pool, -1, "str2job: bad line '%s'", str);
          solv_free(pieces);
          return -1;
        }
          solv_free(pieces);
          return -1;
        }
@@ -1112,7 +1113,7 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp)
     }
   else
     {
     }
   else
     {
-      pool_debug(pool, SOLV_ERROR, "str2job: unknown selection in '%s'\n", str);
+      pool_error(pool, -1, "str2job: unknown selection in '%s'", str);
       solv_free(pieces);
       return -1;
     }
       solv_free(pieces);
       return -1;
     }
@@ -1133,10 +1134,7 @@ addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue)
     if (!strcmp(pieces[0], job2str[i].str))
       break;
   if (!job2str[i].str)
     if (!strcmp(pieces[0], job2str[i].str))
       break;
   if (!job2str[i].str)
-    {
-      pool_debug(pool, SOLV_ERROR, "selstr2job: unknown job '%s'\n", pieces[0]);
-      return -1;
-    }
+    return pool_error(pool, -1, "selstr2job: unknown job '%s'", pieces[0]);
   job = job2str[i].job;
   if (npieces > 3)
     {
   job = job2str[i].job;
   if (npieces > 3)
     {
@@ -1150,10 +1148,7 @@ addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue)
        }
     }
   if (npieces < 4)
        }
     }
   if (npieces < 4)
-    {
-      pool_debug(pool, SOLV_ERROR, "selstr2job: no selection flags\n");
-      return -1;
-    }
+    return pool_error(pool, -1, "selstr2job: no selection flags");
   selflags = str2selflags(pool, pieces[3]);
   queue_init(&sel);
   r = selection_make(pool, &sel, pieces[2], selflags);
   selflags = str2selflags(pool, pieces[3]);
   queue_init(&sel);
   r = selection_make(pool, &sel, pieces[2], selflags);
@@ -1534,10 +1529,7 @@ testcase_setpoolflags(Pool *pool, const char *str)
        if (!strncmp(poolflags2str[i].str, s, p - s) && poolflags2str[i].str[p - s] == 0)
          break;
       if (!poolflags2str[i].str)
        if (!strncmp(poolflags2str[i].str, s, p - s) && poolflags2str[i].str[p - s] == 0)
          break;
       if (!poolflags2str[i].str)
-       {
-         pool_debug(pool, SOLV_ERROR, "setpoolflags: unknown flag '%.*s'\n", (int)(p - s), s);
-         return 0;
-       }
+        return pool_error(pool, 0, "setpoolflags: unknown flag '%.*s'", (int)(p - s), s);
       pool_set_flag(pool, poolflags2str[i].flag, v);
     }
   return 1;
       pool_set_flag(pool, poolflags2str[i].flag, v);
     }
   return 1;
@@ -1591,15 +1583,9 @@ testcase_setsolverflags(Solver *solv, const char *str)
        if (!strncmp(solverflags2str[i].str, s, p - s) && solverflags2str[i].str[p - s] == 0)
          break;
       if (!solverflags2str[i].str)
        if (!strncmp(solverflags2str[i].str, s, p - s) && solverflags2str[i].str[p - s] == 0)
          break;
       if (!solverflags2str[i].str)
-       {
-         pool_debug(solv->pool, SOLV_ERROR, "setsolverflags: unknown flag '%.*s'\n", (int)(p - s), s);
-         return 0;
-       }
+       return pool_error(solv->pool, 0, "setsolverflags: unknown flag '%.*s'", (int)(p - s), s);
       if (solver_set_flag(solv, solverflags2str[i].flag, v) == -1)
       if (solver_set_flag(solv, solverflags2str[i].flag, v) == -1)
-       {
-         pool_debug(solv->pool, SOLV_ERROR, "setsolverflags: unsupported flag '%s'\n", solverflags2str[i].str);
-         return 0;
-       }
+        return pool_error(solv->pool, 0, "setsolverflags: unsupported flag '%s'", solverflags2str[i].str);
     }
   return 1;
 }
     }
   return 1;
 }
@@ -2121,6 +2107,19 @@ testcase_solverresult(Solver *solv, int resultflags)
        }
       queue_free(&whyq);
     }
        }
       queue_free(&whyq);
     }
+  if ((resultflags & TESTCASE_RESULT_CLEANDEPS) != 0)
+    {
+      Queue q;
+
+      queue_init(&q);
+      solver_get_cleandeps(solv, &q);
+      for (i = 0; i < q.count; i++)
+       {
+         s = pool_tmpjoin(pool, "cleandeps ", testcase_solvid2str(pool, q.elements[i]), 0);
+         strqueue_push(&sq, s);
+       }
+      queue_free(&q);
+    }
   strqueue_sort(&sq);
   result = strqueue_join(&sq);
   strqueue_free(&sq);
   strqueue_sort(&sq);
   result = strqueue_join(&sq);
   strqueue_free(&sq);
@@ -2147,10 +2146,7 @@ testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const cha
     resultname = "solver.result";
 
   if (mkdir(dir, 0777) && errno != EEXIST)
     resultname = "solver.result";
 
   if (mkdir(dir, 0777) && errno != EEXIST)
-    {
-      pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not create directory '%s'\n", dir);
-      return 0;
-    }
+    return pool_error(solv->pool, 0, "testcase_write: could not create directory '%s'", dir);
   strqueue_init(&sq);
   FOR_REPOS(repoid, repo)
     {
   strqueue_init(&sq);
   FOR_REPOS(repoid, repo)
     {
@@ -2171,14 +2167,14 @@ testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const cha
       out = pool_tmpjoin(pool, dir, "/", out);
       if (!(fp = solv_xfopen(out, "w")))
        {
       out = pool_tmpjoin(pool, dir, "/", out);
       if (!(fp = solv_xfopen(out, "w")))
        {
-         pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
+         pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out);
          strqueue_free(&sq);
          return 0;
        }
       testcase_write_testtags(repo, fp);
       if (fclose(fp))
        {
          strqueue_free(&sq);
          return 0;
        }
       testcase_write_testtags(repo, fp);
       if (fclose(fp))
        {
-         pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
+         pool_error(solv->pool, 0, "testcase_write: write error");
          strqueue_free(&sq);
          return 0;
        }
          strqueue_free(&sq);
          return 0;
        }
@@ -2303,14 +2299,14 @@ testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const cha
          out = pool_tmpjoin(pool, dir, "/", resultname);
          if (!(fp = fopen(out, "w")))
            {
          out = pool_tmpjoin(pool, dir, "/", resultname);
          if (!(fp = fopen(out, "w")))
            {
-             pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
+             pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out);
              solv_free(result);
              strqueue_free(&sq);
              return 0;
            }
          if (result && *result && fwrite(result, strlen(result), 1, fp) != 1)
            {
              solv_free(result);
              strqueue_free(&sq);
              return 0;
            }
          if (result && *result && fwrite(result, strlen(result), 1, fp) != 1)
            {
-             pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
+             pool_error(solv->pool, 0, "testcase_write: write error");
              solv_free(result);
              strqueue_free(&sq);
              fclose(fp);
              solv_free(result);
              strqueue_free(&sq);
              fclose(fp);
@@ -2318,7 +2314,7 @@ testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const cha
            }
          if (fclose(fp))
            {
            }
          if (fclose(fp))
            {
-             pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
+             pool_error(solv->pool, 0, "testcase_write: write error");
              strqueue_free(&sq);
              return 0;
            }
              strqueue_free(&sq);
              return 0;
            }
@@ -2330,20 +2326,20 @@ testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const cha
   out = pool_tmpjoin(pool, dir, "/", testcasename);
   if (!(fp = fopen(out, "w")))
     {
   out = pool_tmpjoin(pool, dir, "/", testcasename);
   if (!(fp = fopen(out, "w")))
     {
-      pool_debug(solv->pool, SOLV_ERROR, "testcase_write: could not open '%s' for writing\n", out);
+      pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out);
       strqueue_free(&sq);
       return 0;
     }
   if (*cmd && fwrite(cmd, strlen(cmd), 1, fp) != 1)
     {
       strqueue_free(&sq);
       return 0;
     }
   if (*cmd && fwrite(cmd, strlen(cmd), 1, fp) != 1)
     {
-      pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
+      pool_error(solv->pool, 0, "testcase_write: write error");
       strqueue_free(&sq);
       fclose(fp);
       return 0;
     }
   if (fclose(fp))
     {
       strqueue_free(&sq);
       fclose(fp);
       return 0;
     }
   if (fclose(fp))
     {
-      pool_debug(solv->pool, SOLV_ERROR, "testcase_write: write error\n");
+      pool_error(solv->pool, 0, "testcase_write: write error");
       strqueue_free(&sq);
       return 0;
     }
       strqueue_free(&sq);
       return 0;
     }
@@ -2497,7 +2493,7 @@ str2resultflags(Pool *pool, char *s)      /* modifies the string! */
            break;
          }
       if (!resultflags2str[i].str)
            break;
          }
       if (!resultflags2str[i].str)
-       pool_debug(pool, SOLV_ERROR, "result: unknown flag '%s'\n", s);
+       pool_error(pool, 0, "result: unknown flag '%s'", s);
       s = se;
     }
   return resultflags;
       s = se;
     }
   return resultflags;
@@ -2527,7 +2523,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
     *resultflagsp = 0;
   if (!fp && !(fp = fopen(testcase, "r")))
     {
     *resultflagsp = 0;
   if (!fp && !(fp = fopen(testcase, "r")))
     {
-      pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", testcase);
+      pool_error(pool, 0, "testcase_read: could not open '%s'", testcase);
       return 0;
     }
   testcasedir = solv_strdup(testcase);
       return 0;
     }
   testcasedir = solv_strdup(testcase);
@@ -2617,7 +2613,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
                }
              if (!rfp)
                {
                }
              if (!rfp)
                {
-                 pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", rdata);
+                 pool_error(pool, 0, "testcase_read: could not open '%s'", rdata);
                }
              else if (!strcmp(repotype, "testtags"))
                {
                }
              else if (!strcmp(repotype, "testtags"))
                {
@@ -2639,7 +2635,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
              else
                {
                  fclose(rfp);
              else
                {
                  fclose(rfp);
-                 pool_debug(pool, SOLV_ERROR, "testcase_read: unknown repo type for repo '%s'\n", repo->name);
+                 pool_error(pool, 0, "testcase_read: unknown repo type for repo '%s'", repo->name);
                }
            }
        }
                }
            }
        }
@@ -2675,7 +2671,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
                }
              if (!(dp && *dp))
                {
                }
              if (!(dp && *dp))
                {
-                 pool_debug(pool, SOLV_ERROR, "testcase_read: system: could not change disttype to '%s'\n", pieces[2]);
+                 pool_error(pool, 0, "testcase_read: system: could not change disttype to '%s'", pieces[2]);
                  missing_features = 1;
                }
            }
                  missing_features = 1;
                }
            }
@@ -2689,7 +2685,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
            {
              Repo *repo = testcase_str2repo(pool, pieces[3]);
              if (!repo)
            {
              Repo *repo = testcase_str2repo(pool, pieces[3]);
              if (!repo)
-               pool_debug(pool, SOLV_ERROR, "testcase_read: system: unknown repo '%s'\n", pieces[3]);
+               pool_error(pool, 0, "testcase_read: system: unknown repo '%s'", pieces[3]);
              else
                pool_set_installed(pool, repo);
            }
              else
                pool_set_installed(pool, repo);
            }
@@ -2727,7 +2723,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
          s = strchr(pieces[1], '(');
          if (!s && pieces[1][i - 1] != ')')
            {
          s = strchr(pieces[1], '(');
          if (!s && pieces[1][i - 1] != ')')
            {
-             pool_debug(pool, SOLV_ERROR, "testcase_read: bad namespace '%s'\n", pieces[1]);
+             pool_error(pool, 0, "testcase_read: bad namespace '%s'", pieces[1]);
            }
          else
            {
            }
          else
            {
@@ -2790,7 +2786,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
                {
                  FILE *rfp = fopen(rdata, "r");
                  if (!rfp)
                {
                  FILE *rfp = fopen(rdata, "r");
                  if (!rfp)
-                   pool_debug(pool, SOLV_ERROR, "testcase_read: could not open '%s'\n", rdata);
+                   pool_error(pool, 0, "testcase_read: could not open '%s'", rdata);
                  else
                    {
                      result = read_file(rfp);
                  else
                    {
                      result = read_file(rfp);
@@ -2816,7 +2812,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
          Id p;
          if (strcmp(pieces[1], "pkg"))
            {
          Id p;
          if (strcmp(pieces[1], "pkg"))
            {
-             pool_debug(pool, SOLV_ERROR, "testcase_read: bad disable type '%s'\n", pieces[1]);
+             pool_error(pool, 0, "testcase_read: bad disable type '%s'", pieces[1]);
              continue;
            }
          if (!prepared)
              continue;
            }
          if (!prepared)
@@ -2832,7 +2828,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
          if (p)
            MAPCLR(pool->considered, p);
          else
          if (p)
            MAPCLR(pool->considered, p);
          else
-           pool_debug(pool, SOLV_ERROR, "disable: unknown package '%s'\n", pieces[2]);
+           pool_error(pool, 0, "disable: unknown package '%s'", pieces[2]);
        }
       else if (!strcmp(pieces[0], "feature"))
        {
        }
       else if (!strcmp(pieces[0], "feature"))
        {
@@ -2844,7 +2840,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
                  break;
              if (!features[j])
                {
                  break;
              if (!features[j])
                {
-                 pool_debug(pool, SOLV_ERROR, "testcase_read: missing feature '%s'\n", pieces[i]);
+                 pool_error(pool, 0, "testcase_read: missing feature '%s'", pieces[i]);
                  missing_features++;
                }
            }
                  missing_features++;
                }
            }
@@ -2871,12 +2867,12 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
                  break;
              if (!op->flags)
                {
                  break;
              if (!op->flags)
                {
-                 pool_debug(pool, SOLV_ERROR, "testcase_read: genid: unknown op '%s'\n", pieces[2]);
+                 pool_error(pool, 0, "testcase_read: genid: unknown op '%s'", pieces[2]);
                  break;
                }
              if (ngenid < 2)
                {
                  break;
                }
              if (ngenid < 2)
                {
-                 pool_debug(pool, SOLV_ERROR, "testcase_read: genid: out of stack\n");
+                 pool_error(pool, 0, "testcase_read: genid: out of stack");
                  break;
                }
              ngenid -= 2;
                  break;
                }
              ngenid -= 2;
@@ -2890,7 +2886,7 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
            id = testcase_str2dep(pool, pieces[2]);
          else
            {
            id = testcase_str2dep(pool, pieces[2]);
          else
            {
-             pool_debug(pool, SOLV_ERROR, "testcase_read: genid: unknown command '%s'\n", pieces[1]);
+             pool_error(pool, 0, "testcase_read: genid: unknown command '%s'", pieces[1]);
              break;
            }
          genid[ngenid++] = id;
              break;
            }
          genid[ngenid++] = id;
@@ -2899,14 +2895,14 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
        {
          if (strcmp(pieces[1], "name"))
            {
        {
          if (strcmp(pieces[1], "name"))
            {
-             pool_debug(pool, SOLV_ERROR, "testcase_read: autoinst: illegal mode\n");
+             pool_error(pool, 0, "testcase_read: autoinst: illegal mode");
              break;
            }
          queue_push(&autoinstq, pool_str2id(pool, pieces[2], 1));
        }
       else
        {
              break;
            }
          queue_push(&autoinstq, pool_str2id(pool, pieces[2], 1));
        }
       else
        {
-         pool_debug(pool, SOLV_ERROR, "testcase_read: cannot parse command '%s'\n", pieces[0]);
+         pool_error(pool, 0, "testcase_read: cannot parse command '%s'", pieces[0]);
        }
     }
   while (job && ngenid > 0)
        }
     }
   while (job && ngenid > 0)
index 341b9c2..2069637 100644 (file)
@@ -18,6 +18,7 @@
 #define TESTCASE_RESULT_RULES          (1 << 6)
 #define TESTCASE_RESULT_GENID          (1 << 7)
 #define TESTCASE_RESULT_REASON         (1 << 8)
 #define TESTCASE_RESULT_RULES          (1 << 6)
 #define TESTCASE_RESULT_GENID          (1 << 7)
 #define TESTCASE_RESULT_REASON         (1 << 8)
+#define TESTCASE_RESULT_CLEANDEPS      (1 << 9)
 
 /* reuse solver hack, testsolv use only */
 #define TESTCASE_RESULT_REUSE_SOLVER   (1 << 31)
 
 /* reuse solver hack, testsolv use only */
 #define TESTCASE_RESULT_REUSE_SOLVER   (1 << 31)
index 21bdbd1..17b1984 100644 (file)
@@ -1,4 +1,13 @@
 -------------------------------------------------------------------
 -------------------------------------------------------------------
+Mon Oct 23 11:40:22 CEST 2017 - mls@suse.de
+
+- many fixes and improvements for cleandeps
+- support debian packages with xz compressed control.tar
+- always create dup rules for "distupgrade" jobs
+- use recommends also for ordering packages
+- bump version to 0.6.30
+
+-------------------------------------------------------------------
 Thu Sep  7 16:18:20 CEST 2017 - mls@suse.de
 
 - expose solver_get_recommendations in bindings
 Thu Sep  7 16:18:20 CEST 2017 - mls@suse.de
 
 - expose solver_get_recommendations in bindings
index 76ea9f1..35516ba 100644 (file)
@@ -19,7 +19,7 @@ SET (libsolv_SRCS
     queue.c repo.c repodata.c repopage.c util.c policy.c solvable.c
     transaction.c order.c rules.c problems.c linkedpkg.c cplxdeps.c
     chksum.c md5.c sha1.c sha2.c solvversion.c selection.c
     queue.c repo.c repodata.c repopage.c util.c policy.c solvable.c
     transaction.c order.c rules.c problems.c linkedpkg.c cplxdeps.c
     chksum.c md5.c sha1.c sha2.c solvversion.c selection.c
-    fileprovides.c diskusage.c suse.c)
+    fileprovides.c diskusage.c suse.c solver_util.c cleandeps.c)
 
 SET (libsolv_HEADERS
     bitmap.h evr.h hash.h policy.h poolarch.h poolvendor.h pool.h
 
 SET (libsolv_HEADERS
     bitmap.h evr.h hash.h policy.h poolarch.h poolvendor.h pool.h
diff --git a/src/cleandeps.c b/src/cleandeps.c
new file mode 100644 (file)
index 0000000..cbe2020
--- /dev/null
@@ -0,0 +1,1394 @@
+/*
+ * Copyright (c) 2017, SUSE Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * cleandeps.c
+ *
+ * code to find and erase unneeded packages
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "solver.h"
+#include "solverdebug.h"
+#include "solver_private.h"
+#include "bitmap.h"
+#include "pool.h"
+#include "util.h"
+#include "policy.h"
+#include "cplxdeps.h"
+
+#undef CLEANDEPSDEBUG
+
+/*
+ * This functions collects all packages that are looked at
+ * when a dependency is checked. We need it to "pin" installed
+ * packages when removing a supplemented package in createcleandepsmap.
+ * Here's an not uncommon example:
+ *   A contains "Supplements: packageand(B, C)"
+ *   B contains "Requires: A"
+ * Now if we remove C, the supplements is no longer true,
+ * thus we also remove A. Without the dep_pkgcheck function, we
+ * would now also remove B, but this is wrong, as adding back
+ * C doesn't make the supplements true again. Thus we "pin" B
+ * when we remove A.
+ * There's probably a better way to do this, but I haven't come
+ * up with it yet ;)
+ */
+static void
+dep_pkgcheck_slow(Solver *solv, Id dep, Map *m, Queue *q)
+{
+  Pool *pool = solv->pool;
+  Id p, pp;
+
+  if (ISRELDEP(dep))
+    {
+      Reldep *rd = GETRELDEP(pool, dep);
+      if (rd->flags >= 8)
+       {
+         if (rd->flags == REL_AND)
+           {
+             dep_pkgcheck_slow(solv, rd->name, m, q);
+             dep_pkgcheck_slow(solv, rd->evr, m, q);
+             return;
+           }
+         if (rd->flags == REL_COND || rd->flags == REL_UNLESS)
+           {
+             dep_pkgcheck_slow(solv, rd->name, m, q);
+             if (ISRELDEP(rd->evr))
+               {
+                 Reldep *rd2 = GETRELDEP(pool, rd->evr);
+                 if (rd2->flags == REL_ELSE)
+                   dep_pkgcheck_slow(solv, rd2->evr, m, q);
+               }
+             return;
+           }
+         if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
+           return;
+       }
+    }
+  FOR_PROVIDES(p, pp, dep)
+    if (!m || MAPTST(m, p))
+      queue_push(q, p);
+}
+
+static inline void
+dep_pkgcheck(Solver *solv, Id dep, Map *m, Queue *q)
+{
+  Pool *pool = solv->pool;
+  Id p, pp;
+
+  if (!ISSIMPLEDEP(pool, dep))
+    {
+      dep_pkgcheck_slow(solv, dep, m, q);
+      return;
+    }
+  FOR_PROVIDES(p, pp, dep)
+    if (!m || MAPTST(m, p))
+      queue_push(q, p);
+}
+
+static int
+check_xsupp(Solver *solv, Queue *depq, Id dep)
+{
+  Pool *pool = solv->pool;
+  Id p, pp;
+
+  if (ISRELDEP(dep))
+    {
+      Reldep *rd = GETRELDEP(pool, dep);
+      if (rd->flags >= 8)
+       {
+         if (rd->flags == REL_AND)
+           {
+             if (!check_xsupp(solv, depq, rd->name))
+               return 0;
+             return check_xsupp(solv, depq, rd->evr);
+           }
+         if (rd->flags == REL_OR)
+           {
+             if (check_xsupp(solv, depq, rd->name))
+               return 1;
+             return check_xsupp(solv, depq, rd->evr);
+           }
+         if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
+           return 0;
+         if (depq && rd->flags == REL_NAMESPACE)
+           {
+             int i;
+             for (i = 0; i < depq->count; i++)
+               if (depq->elements[i] == dep || depq->elements[i] == rd->name)
+                return 1;
+           }
+       }
+    }
+  FOR_PROVIDES(p, pp, dep)
+    if (p == SYSTEMSOLVABLE || pool->solvables[p].repo == solv->installed)
+      return 1;
+  return 0;
+}
+
+struct trj_data {
+  Queue *edges;
+  Id *low;
+  Id idx;
+  Id nstack;
+  Id firstidx;
+};
+
+/* Tarjan's SCC algorithm, slightly modifed */
+static void
+trj_visit(struct trj_data *trj, Id node)
+{
+  Id *low = trj->low;
+  Queue *edges = trj->edges;
+  Id nnode, myidx, stackstart;
+  int i;
+
+  low[node] = myidx = trj->idx++;
+  low[(stackstart = trj->nstack++)] = node;
+  for (i = edges->elements[node]; (nnode = edges->elements[i]) != 0; i++)
+    {
+      Id l = low[nnode];
+      if (!l)
+       {
+         if (!edges->elements[edges->elements[nnode]])
+           {
+             trj->idx++;
+             low[nnode] = -1;
+             continue;
+           }
+         trj_visit(trj, nnode);
+         l = low[nnode];
+       }
+      if (l < 0)
+       continue;
+      if (l < trj->firstidx)
+       {
+         int k;
+         for (k = l; low[low[k]] == l; k++)
+           low[low[k]] = -1;
+       }
+      else if (l < low[node])
+       low[node] = l;
+    }
+  if (low[node] == myidx)
+    {
+      if (myidx != trj->firstidx)
+       myidx = -1;
+      for (i = stackstart; i < trj->nstack; i++)
+       low[low[i]] = myidx;
+      trj->nstack = stackstart;
+    }
+}
+
+#ifdef ENABLE_COMPLEX_DEPS
+static void
+complex_filter_unneeded(Pool *pool, Id ip, Id req, Queue *edges, Map *cleandepsmap, Queue *unneededq)
+{
+  int i, j;
+  Queue dq;
+  Id p;
+
+  queue_init(&dq);
+  i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
+  if (i == 0 || i == 1)
+    {
+      queue_free(&dq);
+      return;
+    }
+  for (i = 0; i < dq.count; i++)
+    {
+      for (; (p = dq.elements[i]) != 0; i++)
+       {
+         if (p < 0)
+           {
+             if (pool->solvables[-p].repo != pool->installed)
+               break;
+             continue;
+           }
+         if (p == ip || pool->solvables[p].repo != pool->installed || !MAPTST(cleandepsmap, p - pool->installed->start))
+           continue;
+         for (j = 0; j < unneededq->count; j++)
+           if (p == unneededq->elements[j])
+             {
+               if (edges->elements[edges->count - 1] != j + 1)
+                 queue_push(edges, j + 1);
+               break;
+             }
+       }
+      while (dq.elements[i])
+       i++;
+    }
+  queue_free(&dq);
+}
+#endif
+
+
+static void
+filter_unneeded(Solver *solv, Queue *unneededq, Map *unneededmap, int justone)
+{
+  Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
+  Queue edges;
+  Id *nrequires;
+  Map m, installedm;
+  int i, j, pass, count = unneededq->count;
+  Id *low;
+
+  if (unneededq->count < 2)
+    return;
+  map_init(&m, 0);
+  if (!unneededmap)
+    {
+      unneededmap = &m;
+      map_grow(unneededmap, installed->end - installed->start);
+      for (i = 0; i < count; i++)
+       MAPSET(unneededmap, unneededq->elements[i] - installed->start);
+    }
+  map_init(&installedm, pool->nsolvables);
+  for (i = installed->start; i < installed->end; i++)
+    if (pool->solvables[i].repo == installed)
+      MAPSET(&installedm, i);
+
+  nrequires = solv_calloc(count, sizeof(Id));
+  queue_init(&edges);
+  queue_prealloc(&edges, count * 4 + 10);      /* pre-size */
+
+  /*
+   * Go through the solvables in the nodes queue and create edges for
+   * all requires/recommends/supplements between the nodes.
+   * The edges are stored in the edges queue, we add 1 to the node
+   * index so that nodes in the edges queue are != 0 and we can
+   * terminate the edge list with 0.
+   * Thus for node element 5, the edges are stored starting at
+   * edges.elements[6] and are 0-terminated.
+   */
+  /* leave first element zero to make things easier */
+  /* also add trailing zero */
+  queue_insertn(&edges, 0, 1 + count + 1, 0);
+
+  /* first requires and recommends */
+  for (i = 0; i < count; i++)
+    {
+      Solvable *s = pool->solvables + unneededq->elements[i];
+      int oldcount = edges.count;
+      edges.elements[i + 1] = oldcount;
+      for (pass = 0; pass < 2; pass++)
+       {
+         unsigned int off = pass == 0 ? s->requires : s->recommends;
+         Id p, pp, dep, *dp;
+         if (off)
+           for (dp = s->repo->idarraydata + off; (dep = *dp) != 0; dp++)
+             {
+#ifdef ENABLE_COMPLEX_DEPS
+               if (pool_is_complex_dep(pool, dep))
+                 {
+                   complex_filter_unneeded(pool, s - pool->solvables, dep, &edges, unneededmap, unneededq);
+                   continue;
+                 }
+#endif
+               if (justone)
+                 {
+                   int count = 0;
+                   FOR_PROVIDES(p, pp, dep)
+                     {
+                       Solvable *sp = pool->solvables + p;
+                       if (s == sp || sp->repo != installed)
+                         continue;
+                       count++;
+                     }
+                   if (count != 1)
+                     continue;
+                 }
+               FOR_PROVIDES(p, pp, dep)
+                 {
+                   Solvable *sp = pool->solvables + p;
+                   if (s == sp || sp->repo != installed || !MAPTST(unneededmap, p - installed->start))
+                     continue;
+                   for (j = 0; j < count; j++)
+                     if (p == unneededq->elements[j])
+                       {
+                         if (edges.elements[edges.count - 1] != j + 1)
+                           queue_push(&edges, j + 1);
+                       }
+                 }
+             }
+         if (pass == 0)
+           nrequires[i] = edges.count - oldcount;
+       }
+      queue_push(&edges, 0);
+    }
+#if 0
+  printf("requires + recommends\n");
+  for (i = 0; i < count; i++)
+    {
+      int j;
+      printf("  %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]);
+      for (j = edges.elements[i + 1]; edges.elements[j]; j++)
+       printf("    - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1]));
+    }
+#endif
+
+  /* then add supplements */
+  for (i = 0; i < count; i++)
+    {
+      Solvable *s = pool->solvables + unneededq->elements[i];
+      if (s->supplements)
+       {
+         Id *dp;
+         int k;
+         for (dp = s->repo->idarraydata + s->supplements; *dp; dp++)
+           if (solver_dep_possible(solv, *dp, &installedm))
+             {
+               Queue iq;
+               Id iqbuf[16];
+               queue_init_buffer(&iq, iqbuf, sizeof(iqbuf)/sizeof(*iqbuf));
+               dep_pkgcheck(solv, *dp, 0, &iq);
+               if (justone && iq.count != 1)
+                 {
+                   queue_free(&iq);
+                   continue;
+                 }
+               for (k = 0; k < iq.count; k++)
+                 {
+                   Id p = iq.elements[k];
+                   Solvable *sp = pool->solvables + p;
+                   if (p == unneededq->elements[i] || sp->repo != installed || !MAPTST(unneededmap, p - installed->start))
+                     continue;
+                   for (j = 0; j < count; j++)
+                     if (p == unneededq->elements[j])
+                       break;
+                   /* now add edge from j + 1 to i + 1 */
+                   queue_insert(&edges, edges.elements[j + 1] + nrequires[j], i + 1);
+                   /* addapt following edge pointers */
+                   for (j = j + 2; j < count + 1; j++)
+                     edges.elements[j]++;
+                 }
+               queue_free(&iq);
+             }
+       }
+    }
+#if 0
+  /* print result */
+  printf("+ supplements\n");
+  for (i = 0; i < count; i++)
+    {
+      int j;
+      printf("  %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]);
+      for (j = edges.elements[i + 1]; edges.elements[j]; j++)
+       printf("    - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1]));
+    }
+#endif
+  map_free(&installedm);
+
+  /* now run SCC algo two times, first with requires+recommends+supplements,
+   * then again without the requires. We run it the second time to get rid
+   * of packages that got dragged in via recommends/supplements */
+  /*
+   * low will contain the result of the SCC search.
+   * it must be of at least size 2 * (count + 1) and
+   * must be zero initialized.
+   * The layout is:
+   *    0  low low ... low stack stack ...stack 0
+   *            count              count
+   */
+  low = solv_calloc(count + 1, 2 * sizeof(Id));
+  for (pass = 0; pass < 2; pass++)
+    {
+      struct trj_data trj;
+      if (pass)
+       {
+         memset(low, 0, (count + 1) * (2 * sizeof(Id)));
+         for (i = 0; i < count; i++)
+           {
+             edges.elements[i + 1] += nrequires[i];
+             if (!unneededq->elements[i])
+               low[i + 1] = -1;        /* ignore this node */
+           }
+       }
+      trj.edges = &edges;
+      trj.low = low;
+      trj.idx = count + 1;     /* stack starts here */
+      for (i = 1; i <= count; i++)
+       {
+         if (low[i])
+           continue;
+         if (edges.elements[edges.elements[i]])
+           {
+             trj.firstidx = trj.nstack = trj.idx;
+             trj_visit(&trj, i);
+           }
+         else
+           {
+             Id myidx = trj.idx++;
+             low[i] = myidx;
+             low[myidx] = i;
+           }
+       }
+      /* prune packages */
+      for (i = 0; i < count; i++)
+       if (low[i + 1] <= 0)
+         unneededq->elements[i] = 0;
+    }
+  solv_free(low);
+  solv_free(nrequires);
+  queue_free(&edges);
+
+  /* finally remove all pruned entries from unneededq */
+  for (i = j = 0; i < count; i++)
+    if (unneededq->elements[i])
+      unneededq->elements[j++] = unneededq->elements[i];
+  queue_truncate(unneededq, j);
+  map_free(&m);
+}
+
+
+#ifdef ENABLE_COMPLEX_DEPS
+static void
+complex_cleandeps_remove(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq)
+{
+  int i;
+  Queue dq;
+  Id p;
+
+  queue_init(&dq);
+  i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
+  if (i == 0 || i == 1)
+    {
+      queue_free(&dq);
+      return;
+    }
+  for (i = 0; i < dq.count; i++)
+    {
+      for (; (p = dq.elements[i]) != 0; i++)
+       {
+         if (p < 0)
+           {
+             if (!MAPTST(installedm, -p))
+               break;
+             continue;
+           }
+         if (p != SYSTEMSOLVABLE && MAPTST(im, p))
+           {
+#ifdef CLEANDEPSDEBUG
+             printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+             queue_push(iq, p);
+           }
+       }
+      while (dq.elements[i])
+       i++;
+    }
+  queue_free(&dq);
+}
+
+static void
+complex_cleandeps_addback(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq, Map *userinstalled)
+{
+  int i, blk;
+  Queue dq;
+  Id p;
+
+  queue_init(&dq);
+  i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
+  if (i == 0 || i == 1)
+    {
+      queue_free(&dq);
+      return;
+    }
+  for (i = 0; i < dq.count; i++)
+    {
+      blk = i;
+      for (; (p = dq.elements[i]) != 0; i++)
+       {
+         if (p < 0)
+           {
+             if (!MAPTST(installedm, -p))
+               break;
+           }
+         else if (p == ip)
+           break;
+       }
+      if (!p)
+       {
+         for (i = blk; (p = dq.elements[i]) != 0; i++)
+           {
+             if (p < 0)
+               continue;
+             if (MAPTST(im, p))
+               continue;
+             if (!MAPTST(installedm, p))
+               continue;
+             if (p == ip || MAPTST(userinstalled, p - pool->installed->start))
+               continue;
+#ifdef CLEANDEPSDEBUG
+             printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+             MAPSET(im, p);
+             queue_push(iq, p);
+           }
+       }
+      while (dq.elements[i])
+       i++;
+    }
+  queue_free(&dq);
+}
+
+#endif
+
+static inline int
+queue_contains(Queue *q, Id id)
+{
+  int i;
+  for (i = 0; i < q->count; i++)
+    if (q->elements[i] == id)
+      return 1;
+  return 0;
+}
+
+static void
+find_update_seeds(Solver *solv, Queue *updatepkgs_filtered, Map *userinstalled)
+{
+  Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
+  Queue *cleandeps_updatepkgs = solv->cleandeps_updatepkgs;
+  int i, j;
+  Id p;
+
+  queue_prealloc(updatepkgs_filtered, cleandeps_updatepkgs->count);
+  for (i = 0; i < cleandeps_updatepkgs->count; i++)
+    {
+      p = cleandeps_updatepkgs->elements[i];
+      if (pool->solvables[p].repo == installed)
+       {
+#ifdef ENABLE_LINKED_PKGS
+         const char *name = pool_id2str(pool, pool->solvables[p].name);
+         if (strncmp(name, "pattern:", 8) == 0 || strncmp(name, "application:", 12) == 0)
+           continue;
+#endif
+         queue_push(updatepkgs_filtered, p);
+       }
+    }
+#ifdef CLEANDEPSDEBUG
+  printf("SEEDS IN (%d)\n", updatepkgs_filtered->count);
+  for (i = 0; i < updatepkgs_filtered->count; i++)
+    printf("  - %s\n", pool_solvid2str(pool, updatepkgs_filtered->elements[i]));
+#endif
+  filter_unneeded(solv, updatepkgs_filtered, 0, 1);
+#ifdef CLEANDEPSDEBUG
+  printf("SEEDS OUT (%d)\n", updatepkgs_filtered->count);
+  for (i = 0; i < updatepkgs_filtered->count; i++)
+    printf("  - %s\n", pool_solvid2str(pool, updatepkgs_filtered->elements[i]));
+#endif
+  /* make sure userinstalled packages are in the seeds */
+  for (i = j = 0; i < updatepkgs_filtered->count; i++)
+    {
+      p = updatepkgs_filtered->elements[i];
+      if (!MAPTST(userinstalled, p - installed->start))
+       updatepkgs_filtered->elements[j++] = p;
+    }
+  queue_truncate(updatepkgs_filtered, j);
+  for (i = 0; i < cleandeps_updatepkgs->count; i++)
+    {
+      p = cleandeps_updatepkgs->elements[i];
+      if (pool->solvables[p].repo == installed)
+       {
+#ifdef ENABLE_LINKED_PKGS
+         const char *name = pool_id2str(pool, pool->solvables[p].name);
+         if (strncmp(name, "pattern:", 8) == 0 || strncmp(name, "application:", 12) == 0)
+           {
+             queue_push(updatepkgs_filtered, p);
+             continue;
+           }
+#endif
+         if (MAPTST(userinstalled, p - installed->start))
+           queue_push(updatepkgs_filtered, p);
+       }
+    }
+#ifdef CLEANDEPSDEBUG
+  printf("SEEDS FINAL\n");
+  for (i = 0; i < updatepkgs_filtered->count; i++)
+    printf("  - %s\n", pool_solvid2str(pool, updatepkgs_filtered->elements[i]));
+#endif
+}
+
+/*
+ * Find all installed packages that are no longer
+ * needed regarding the current solver job.
+ *
+ * The algorithm is:
+ * - remove pass: remove all packages that could have
+ *   been dragged in by the obsoleted packages.
+ *   i.e. if package A is obsolete and contains "Requires: B",
+ *   also remove B, as installing A will have pulled in B.
+ *   after this pass, we have a set of still installed packages
+ *   with broken dependencies.
+ * - add back pass:
+ *   now add back all packages that the still installed packages
+ *   require.
+ *
+ * The cleandeps packages are the packages removed in the first
+ * pass and not added back in the second pass.
+ *
+ * If we search for unneeded packages (unneeded is true), we
+ * simply remove all packages except the userinstalled ones in
+ * the first pass.
+ */
+void
+solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
+{
+  Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
+  Queue *job = &solv->job;
+  Map userinstalled;
+  Map im;
+  Map installedm;
+  Rule *r;
+  Id rid, how, what, select;
+  Id p, pp, ip, jp;
+  Id req, *reqp, sup, *supp;
+  Solvable *s;
+  Queue iq, iqcopy, xsuppq;
+  Queue updatepkgs_filtered;
+  int i;
+
+  map_empty(cleandepsmap);
+  if (!installed || installed->end == installed->start)
+    return;
+  map_init(&userinstalled, installed->end - installed->start);
+  map_init(&im, pool->nsolvables);
+  map_init(&installedm, pool->nsolvables);
+  queue_init(&iq);
+  queue_init(&xsuppq);
+
+  for (i = 0; i < job->count; i += 2)
+    {
+      how = job->elements[i];
+      if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
+       {
+         what = job->elements[i + 1];
+         select = how & SOLVER_SELECTMASK;
+         if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
+           {
+             FOR_REPO_SOLVABLES(installed, p, s)
+               MAPSET(&userinstalled, p - installed->start);
+           }
+         FOR_JOB_SELECT(p, pp, select, what)
+           if (pool->solvables[p].repo == installed)
+             MAPSET(&userinstalled, p - installed->start);
+       }
+      if ((how & (SOLVER_JOBMASK | SOLVER_SELECTMASK)) == (SOLVER_ERASE | SOLVER_SOLVABLE_PROVIDES))
+       {
+         what = job->elements[i + 1];
+         if (ISRELDEP(what))
+           {
+             Reldep *rd = GETRELDEP(pool, what);
+             if (rd->flags != REL_NAMESPACE)
+               continue;
+             if (rd->evr == 0)
+               {
+                 queue_pushunique(&iq, rd->name);
+                 continue;
+               }
+             FOR_PROVIDES(p, pp, what)
+               if (p)
+                 break;
+             if (p)
+               continue;
+             queue_pushunique(&iq, what);
+           }
+       }
+    }
+
+  /* have special namespace cleandeps erases */
+  if (iq.count)
+    {
+      for (ip = installed->start; ip < installed->end; ip++)
+       {
+         s = pool->solvables + ip;
+         if (s->repo != installed)
+           continue;
+         if (!s->supplements)
+           continue;
+         supp = s->repo->idarraydata + s->supplements;
+         while ((sup = *supp++) != 0)
+           if (ISRELDEP(sup) && check_xsupp(solv, &iq, sup) && !check_xsupp(solv, 0, sup))
+             {
+#ifdef CLEANDEPSDEBUG
+               printf("xsupp %s from %s\n", pool_dep2str(pool, sup), pool_solvid2str(pool, ip));
+#endif
+               queue_pushunique(&xsuppq, sup);
+             }
+       }
+      queue_empty(&iq);
+    }
+
+  /* also add visible patterns to userinstalled for openSUSE */
+  if (1)
+    {
+      Dataiterator di;
+      dataiterator_init(&di, pool, 0, 0, SOLVABLE_ISVISIBLE, 0, 0);
+      while (dataiterator_step(&di))
+       {
+         Id *dp;
+         if (di.solvid <= 0)
+           continue;
+         s = pool->solvables + di.solvid;
+         if (!s->repo || !s->requires)
+           continue;
+         if (s->repo != installed && !pool_installable(pool, s))
+           continue;
+         if (strncmp(pool_id2str(pool, s->name), "pattern:", 8) != 0)
+           continue;
+         dp = s->repo->idarraydata + s->requires;
+         for (dp = s->repo->idarraydata + s->requires; *dp; dp++)
+           FOR_PROVIDES(p, pp, *dp)
+             if (pool->solvables[p].repo == installed)
+               {
+                 if (strncmp(pool_id2str(pool, pool->solvables[p].name), "pattern", 7) != 0)
+                   continue;
+                 MAPSET(&userinstalled, p - installed->start);
+               }
+       }
+      dataiterator_free(&di);
+    }
+  if (1)
+    {
+      /* all products and their buddies are userinstalled */
+      for (p = installed->start; p < installed->end; p++)
+       {
+         Solvable *s = pool->solvables + p;
+         if (s->repo != installed)
+           continue;
+         if (!strncmp("product:", pool_id2str(pool, s->name), 8))
+           {
+             MAPSET(&userinstalled, p - installed->start);
+#ifdef ENABLE_LINKED_PKGS
+             if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1)
+               {
+                 Id buddy = solv->instbuddy[p - installed->start];
+                 if (buddy >= installed->start && buddy < installed->end)
+                   MAPSET(&userinstalled, buddy - installed->start);
+               }
+#endif
+           }
+       }
+    }
+
+  /* add all positive elements (e.g. locks) to "userinstalled" */
+  for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
+    {
+      r = solv->rules + rid;
+      if (r->d < 0)
+       continue;
+      i = solv->ruletojob.elements[rid - solv->jobrules];
+      if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS)
+       continue;
+      FOR_RULELITERALS(p, jp, r)
+       if (p > 0 && pool->solvables[p].repo == installed)
+         MAPSET(&userinstalled, p - installed->start);
+    }
+
+  /* add all cleandeps candidates to iq */
+  for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
+    {
+      r = solv->rules + rid;
+      if (r->d < 0)                            /* disabled? */
+       continue;
+      if (r->d == 0 && r->p < 0 && r->w2 == 0) /* negative assertion (erase job)? */
+       {
+         p = -r->p;
+         if (pool->solvables[p].repo != installed)
+           continue;
+         MAPCLR(&userinstalled, p - installed->start);
+         if (unneeded)
+           continue;
+         i = solv->ruletojob.elements[rid - solv->jobrules];
+         how = job->elements[i];
+         if ((how & (SOLVER_JOBMASK|SOLVER_CLEANDEPS)) == (SOLVER_ERASE|SOLVER_CLEANDEPS))
+           queue_push(&iq, p);
+       }
+      else if (r->p > 0)                       /* install job */
+       {
+         if (unneeded)
+           continue;
+         i = solv->ruletojob.elements[rid - solv->jobrules];
+         if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS)
+           {
+             /* check if the literals all obsolete some installed package */
+             Map om;
+             int iqstart;
+
+             /* just one installed literal */
+             if (r->d == 0 && r->w2 == 0 && pool->solvables[r->p].repo == installed)
+               continue;
+             /* multiversion is bad */
+             if (solv->multiversion.size && !solv->keepexplicitobsoletes)
+               {
+                 FOR_RULELITERALS(p, jp, r)
+                   if (MAPTST(&solv->multiversion, p))
+                     break;
+                 if (p)
+                   continue;
+               }
+
+             om.size = 0;
+             iqstart = iq.count;
+             FOR_RULELITERALS(p, jp, r)
+               {
+                 if (p < 0)
+                   {
+                     queue_truncate(&iq, iqstart);     /* abort */
+                     break;
+                   }
+                 if (pool->solvables[p].repo == installed)
+                   {
+                     if (iq.count == iqstart)
+                       queue_push(&iq, p);
+                     else
+                       {
+                         for (i = iqstart; i < iq.count; i++)
+                           if (iq.elements[i] == p)
+                             break;
+                         queue_truncate(&iq, iqstart);
+                         if (i < iq.count)
+                           queue_push(&iq, p);
+                       }
+                   }
+                 else
+                   solver_intersect_obsoleted(solv, p, &iq, iqstart, &om);
+                 if (iq.count == iqstart)
+                   break;
+               }
+             if (om.size)
+               map_free(&om);
+           }
+       }
+    }
+  queue_init_clone(&iqcopy, &iq);
+
+  if (!unneeded)
+    {
+      if (solv->cleandeps_updatepkgs)
+       for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
+         queue_push(&iq, solv->cleandeps_updatepkgs->elements[i]);
+    }
+
+  if (unneeded)
+    queue_empty(&iq);  /* just in case... */
+
+  /* clear userinstalled bit for the packages we really want to delete/update */
+  for (i = 0; i < iq.count; i++)
+    {
+      p = iq.elements[i];
+      if (pool->solvables[p].repo != installed)
+       continue;
+      MAPCLR(&userinstalled, p - installed->start);
+    }
+
+  for (p = installed->start; p < installed->end; p++)
+    {
+      if (pool->solvables[p].repo != installed)
+       continue;
+      MAPSET(&installedm, p);
+      if (pool->considered && !MAPTST(pool->considered, p))
+       MAPSET(&userinstalled, p - installed->start);   /* we may not remove those */
+      if (unneeded && !MAPTST(&userinstalled, p - installed->start))
+       continue;
+      MAPSET(&im, p);
+    }
+  MAPSET(&installedm, SYSTEMSOLVABLE);
+  MAPSET(&im, SYSTEMSOLVABLE);
+
+  if (!unneeded && solv->cleandeps_updatepkgs)
+    {
+      /* find update "seeds" */
+      queue_init(&updatepkgs_filtered);
+      find_update_seeds(solv, &updatepkgs_filtered, &userinstalled);
+    }
+
+#ifdef CLEANDEPSDEBUG
+  printf("REMOVE PASS\n");
+#endif
+
+  for (;;)
+    {
+      if (!iq.count)
+       {
+         if (unneeded)
+           break;
+         /* supplements pass */
+         for (ip = installed->start; ip < installed->end; ip++)
+           {
+             if (!MAPTST(&installedm, ip))
+               continue;
+             s = pool->solvables + ip;
+             if (!s->supplements)
+               continue;
+             if (!MAPTST(&im, ip))
+               continue;
+             if (MAPTST(&userinstalled, ip - installed->start))
+               continue;
+             supp = s->repo->idarraydata + s->supplements;
+             while ((sup = *supp++) != 0)
+               if (solver_dep_possible(solv, sup, &im))
+                 break;
+             if (!sup)
+               {
+                 supp = s->repo->idarraydata + s->supplements;
+                 while ((sup = *supp++) != 0)
+                   if (solver_dep_possible(solv, sup, &installedm) || (xsuppq.count && queue_contains(&xsuppq, sup)))
+                     {
+                       /* no longer supplemented, also erase */
+                       int iqcount = iq.count;
+                       /* pin packages, see comment above dep_pkgcheck */
+                       dep_pkgcheck(solv, sup, &im, &iq);
+                       for (i = iqcount; i < iq.count; i++)
+                         {
+                           Id pqp = iq.elements[i];
+                           if (pool->solvables[pqp].repo == installed)
+                             MAPSET(&userinstalled, pqp - installed->start);
+                         }
+                       queue_truncate(&iq, iqcount);
+#ifdef CLEANDEPSDEBUG
+                       printf("%s supplemented [%s]\n", pool_solvid2str(pool, ip), pool_dep2str(pool, sup));
+#endif
+                       queue_push(&iq, ip);
+                     }
+               }
+           }
+         if (!iq.count)
+           break;      /* no supplementing package found, we're done */
+       }
+      ip = queue_shift(&iq);
+      s = pool->solvables + ip;
+      if (!MAPTST(&im, ip))
+       continue;
+      if (!MAPTST(&installedm, ip))
+       continue;
+      if (s->repo == installed && MAPTST(&userinstalled, ip - installed->start))
+       continue;
+      MAPCLR(&im, ip);
+#ifdef CLEANDEPSDEBUG
+      printf("removing %s\n", pool_solvable2str(pool, s));
+#endif
+      if (s->requires)
+       {
+         reqp = s->repo->idarraydata + s->requires;
+         while ((req = *reqp++) != 0)
+           {
+             if (req == SOLVABLE_PREREQMARKER)
+               continue;
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, req))
+               {
+                 complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq);
+                 continue;
+               }
+#endif
+             FOR_PROVIDES(p, pp, req)
+               {
+                 if (p != SYSTEMSOLVABLE && MAPTST(&im, p))
+                   {
+#ifdef CLEANDEPSDEBUG
+                     printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+                     queue_push(&iq, p);
+                   }
+               }
+           }
+       }
+      if (s->recommends)
+       {
+         reqp = s->repo->idarraydata + s->recommends;
+         while ((req = *reqp++) != 0)
+           {
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, req))
+               {
+                 complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq);
+                 continue;
+               }
+#endif
+             FOR_PROVIDES(p, pp, req)
+               {
+                 if (p != SYSTEMSOLVABLE && MAPTST(&im, p))
+                   {
+#ifdef CLEANDEPSDEBUG
+                     printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+                     queue_push(&iq, p);
+                   }
+               }
+           }
+       }
+    }
+
+  /* turn userinstalled into remove set for pruning */
+  map_empty(&userinstalled);
+  for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
+    {
+      r = solv->rules + rid;
+      if (r->p >= 0 || r->d != 0 || r->w2 != 0)
+       continue;       /* disabled or not erase */
+      p = -r->p;
+      MAPCLR(&im, p);
+      if (pool->solvables[p].repo == installed)
+        MAPSET(&userinstalled, p - installed->start);
+    }
+  if (!unneeded && solv->cleandeps_updatepkgs)
+    {
+      for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
+       {
+         p = solv->cleandeps_updatepkgs->elements[i];
+         if (pool->solvables[p].repo == installed)
+           MAPSET(&userinstalled, p - installed->start);
+       }
+    }
+  MAPSET(&im, SYSTEMSOLVABLE); /* in case we cleared it above */
+  for (p = installed->start; p < installed->end; p++)
+    if (MAPTST(&im, p))
+      queue_push(&iq, p);
+  for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
+    {
+      r = solv->rules + rid;
+      if (r->d < 0)
+       continue;
+      FOR_RULELITERALS(p, jp, r)
+       if (p > 0)
+          queue_push(&iq, p);
+    }
+  /* also put directly addressed packages on the install queue
+   * so we can mark patterns as installed */
+  for (i = 0; i < job->count; i += 2)
+    {
+      how = job->elements[i];
+      if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
+       {
+         what = job->elements[i + 1];
+         select = how & SOLVER_SELECTMASK;
+         if (select == SOLVER_SOLVABLE && pool->solvables[what].repo != installed)
+            queue_push(&iq, what);
+       }
+    }
+
+#ifdef CLEANDEPSDEBUG
+  printf("ADDBACK PASS\n");
+#endif
+  for (;;)
+    {
+      if (!iq.count)
+       {
+         /* supplements pass */
+         for (ip = installed->start; ip < installed->end; ip++)
+           {
+             if (!MAPTST(&installedm, ip))
+               continue;
+             if (MAPTST(&userinstalled, ip - installed->start))
+               continue;
+             s = pool->solvables + ip;
+             if (!s->supplements)
+               continue;
+             if (MAPTST(&im, ip))
+               continue;
+             supp = s->repo->idarraydata + s->supplements;
+             while ((sup = *supp++) != 0)
+               if (solver_dep_possible(solv, sup, &im))
+                 break;
+             if (sup)
+               {
+#ifdef CLEANDEPSDEBUG
+                 printf("%s supplemented\n", pool_solvid2str(pool, ip));
+#endif
+                 MAPSET(&im, ip);
+                 queue_push(&iq, ip);
+               }
+           }
+         if (!iq.count)
+           break;
+       }
+      ip = queue_shift(&iq);
+      s = pool->solvables + ip;
+#ifdef CLEANDEPSDEBUG
+      printf("adding back %s\n", pool_solvable2str(pool, s));
+#endif
+      if (s->repo == installed && pool->implicitobsoleteusescolors)
+       {
+         Id a, bestarch = 0;
+         FOR_PROVIDES(p, pp, s->name)
+           {
+             Solvable *ps = pool->solvables + p;
+             if (ps->name != s->name || ps->repo == installed)
+               continue;
+             a = ps->arch;
+             a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
+             if (a && a != 1 && (!bestarch || a < bestarch))
+               bestarch = a;
+           }
+         if (bestarch && (s->arch > pool->lastarch || pool->id2arch[s->arch] != bestarch))
+           {
+             FOR_PROVIDES(p, pp, s->name)
+               {
+                 Solvable *ps = pool->solvables + p;
+                 if (ps->repo == installed && ps->name == s->name && ps->evr == s->evr && ps->arch != s->arch && ps->arch < pool->lastarch && pool->id2arch[ps->arch] == bestarch)
+                   if (!MAPTST(&im, p))
+                     {
+#ifdef CLEANDEPSDEBUG
+                       printf("%s lockstep %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+                       MAPSET(&im, p);
+                       queue_push(&iq, p);
+                     }
+               }
+           }
+       }
+      if (s->requires)
+       {
+         reqp = s->repo->idarraydata + s->requires;
+         while ((req = *reqp++) != 0)
+           {
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, req))
+               {
+                 complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled);
+                 continue;
+               }
+#endif
+             FOR_PROVIDES(p, pp, req)
+               if (p == ip)
+                 break;
+             if (p)
+               continue;
+             FOR_PROVIDES(p, pp, req)
+               {
+                 if (MAPTST(&im, p))
+                   continue;
+                 if (MAPTST(&installedm, p))
+                   {
+                     if (p == ip)
+                       continue;
+                     if (MAPTST(&userinstalled, p - installed->start))
+                       continue;
+#ifdef CLEANDEPSDEBUG
+                     printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+                     MAPSET(&im, p);
+                     queue_push(&iq, p);
+                   }
+               }
+           }
+       }
+      if (s->recommends)
+       {
+         reqp = s->repo->idarraydata + s->recommends;
+         while ((req = *reqp++) != 0)
+           {
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, req))
+               {
+                 complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled);
+                 continue;
+               }
+#endif
+             FOR_PROVIDES(p, pp, req)
+               if (p == ip)
+                 break;
+             if (p)
+               continue;
+             FOR_PROVIDES(p, pp, req)
+               {
+                 if (MAPTST(&im, p))
+                   continue;
+                 if (MAPTST(&installedm, p))
+                   {
+                     if (p == ip)
+                       continue;
+                     if (MAPTST(&userinstalled, p - installed->start))
+                       continue;
+#ifdef CLEANDEPSDEBUG
+                     printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+                     MAPSET(&im, p);
+                     queue_push(&iq, p);
+                   }
+               }
+           }
+       }
+    }
+
+  queue_free(&iq);
+  /* make sure the updatepkgs and mistakes are not in the cleandeps map */
+  if (!unneeded && solv->cleandeps_updatepkgs)
+    {
+      for (i = 0; i < updatepkgs_filtered.count; i++)
+        MAPSET(&im, updatepkgs_filtered.elements[i]);
+      queue_free(&updatepkgs_filtered);
+    }
+  if (solv->cleandeps_mistakes)
+    for (i = 0; i < solv->cleandeps_mistakes->count; i++)
+      MAPSET(&im, solv->cleandeps_mistakes->elements[i]);
+  /* also remove original iq packages */
+  for (i = 0; i < iqcopy.count; i++)
+    MAPSET(&im, iqcopy.elements[i]);
+  queue_free(&iqcopy);
+  for (p = installed->start; p < installed->end; p++)
+    {
+      if (pool->solvables[p].repo != installed)
+       continue;
+      if (pool->considered && !MAPTST(pool->considered, p))
+          continue;
+      if (!MAPTST(&im, p))
+        MAPSET(cleandepsmap, p - installed->start);
+    }
+  map_free(&im);
+  map_free(&installedm);
+  map_free(&userinstalled);
+  queue_free(&xsuppq);
+#ifdef CLEANDEPSDEBUG
+  printf("=== final cleandeps map:\n");
+  for (p = installed->start; p < installed->end; p++)
+    if (MAPTST(cleandepsmap, p - installed->start))
+      printf("  - %s\n", pool_solvid2str(pool, p));
+#endif
+}
+
+void
+solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered)
+{
+  Repo *installed = solv->installed;
+  int i;
+  Map cleandepsmap;
+
+  queue_empty(unneededq);
+  if (!installed || installed->end == installed->start)
+    return;
+
+  map_init(&cleandepsmap, installed->end - installed->start);
+  solver_createcleandepsmap(solv, &cleandepsmap, 1);
+  for (i = installed->start; i < installed->end; i++)
+    if (MAPTST(&cleandepsmap, i - installed->start))
+      queue_push(unneededq, i);
+
+  if (filtered)
+    filter_unneeded(solv, unneededq, &cleandepsmap, 0);
+  map_free(&cleandepsmap);
+}
+
+static void
+add_cleandeps_mistake(Solver *solv, Id p)
+{
+ if (!solv->cleandeps_mistakes)
+    {    
+      solv->cleandeps_mistakes = solv_calloc(1, sizeof(Queue));
+      queue_init(solv->cleandeps_mistakes);
+    }    
+  queue_push(solv->cleandeps_mistakes, p); 
+  MAPCLR(&solv->cleandepsmap, p - solv->installed->start);
+  solver_reenablepolicyrules_cleandeps(solv, p); 
+}
+
+static inline int
+cleandeps_rule_is_true(Solver *solv, Rule *r)
+{
+  Pool *pool = solv->pool;
+  Id p, pp;
+  FOR_RULELITERALS(p, pp, r)
+    if (p > 0 && solv->decisionmap[p] > 0)
+      return 1;
+  return 0;
+}
+
+int
+solver_check_cleandeps_mistakes(Solver *solv)
+{
+  Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
+  Rule *fr;
+  int i, j, nj;
+  int mademistake = 0;
+
+  if (!solv->cleandepsmap.size || !installed)
+    return 0;
+  /* check for mistakes */
+  policy_update_recommendsmap(solv);
+  for (i = installed->start; i < installed->end; i++)
+    {
+      if (pool->solvables[i].repo != installed)
+       continue;
+      if (solv->decisionmap[i] > 0)
+       {
+         Id req, *reqp;
+         Solvable *s = pool->solvables + i;
+         /* kept package, check requires. we need to do this for things like requires(pre) */
+         reqp = s->repo->idarraydata + s->requires;
+         while ((req = *reqp++) != 0)  
+           {
+             Id p2, pp2;
+             FOR_PROVIDES(p2, pp2, req)
+               {
+                 if (pool->solvables[p2].repo != installed)
+                   continue;
+                 if (p2 == i || solv->decisionmap[p2] > 0)
+                   continue;
+                 if (!MAPTST(&solv->cleandepsmap, p2 - installed->start))
+                   continue;
+                 POOL_DEBUG(SOLV_DEBUG_SOLVER, "cleandeps requires mistake: %s %s %s\n", pool_solvid2str(pool, i), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
+                 add_cleandeps_mistake(solv, p2);
+                 mademistake = 1;
+               }
+           }
+       }
+      if (!MAPTST(&solv->cleandepsmap, i - installed->start))
+       continue;
+      /* a mistake is when the featurerule is true but the updaterule is false */
+      fr = solv->rules + solv->featurerules + (i - installed->start);
+      if (!fr->p)
+        fr = solv->rules + solv->updaterules + (i - installed->start);
+      if (!fr->p)
+       continue;
+      if (!cleandeps_rule_is_true(solv, fr))
+       {
+         /* feature rule is not true, thus we cleandeps erased the package */
+         /* check if the package is recommended/supplemented. if yes, we made a mistake. */
+         if (!MAPTST(&solv->recommendsmap, i) && !solver_is_supplementing(solv, pool->solvables + i))
+           continue;   /* feature rule is not true */
+         POOL_DEBUG(SOLV_DEBUG_SOLVER, "cleandeps recommends mistake: ");
+         solver_printruleclass(solv, SOLV_DEBUG_SOLVER, fr);
+       }
+      else
+       {
+         Rule *r = solv->rules + solv->updaterules + (i - installed->start);
+         if (!r->p || r == fr || cleandeps_rule_is_true(solv, r))
+           {
+             /* update rule is true, check best rules */
+             if (!solv->bestrules_pkg)
+               continue;
+             nj = solv->bestrules_end - solv->bestrules;
+             for (j = 0; j < nj; j++)
+               if (solv->bestrules_pkg[j] == i)
+                 {
+                   r = solv->rules + solv->bestrules + j;
+                   if (!cleandeps_rule_is_true(solv, r))
+                     break;
+                 }
+             if (j == nj)
+               continue;
+           }
+         POOL_DEBUG(SOLV_DEBUG_SOLVER, "cleandeps mistake: ");
+         solver_printruleclass(solv, SOLV_DEBUG_SOLVER, r);
+         POOL_DEBUG(SOLV_DEBUG_SOLVER, "feature rule: ");
+         solver_printruleclass(solv, SOLV_DEBUG_SOLVER, fr);
+       }
+      add_cleandeps_mistake(solv, i);
+      mademistake = 1;
+    }
+  return mademistake;
+}
index d65c0d6..337cd19 100644 (file)
@@ -425,4 +425,5 @@ SOLV_1.0 {
 
 SOLV_1.1 {
                pool_best_solvables;
 
 SOLV_1.1 {
                pool_best_solvables;
+               solver_get_cleandeps;
 } SOLV_1.0;
 } SOLV_1.0;
index 4fe3e08..02e11d1 100644 (file)
@@ -43,8 +43,10 @@ struct _TransactionOrderdata {
 #define TYPE_REQ_P     (1<<2)
 #define TYPE_PREREQ_P  (1<<3)
 
 #define TYPE_REQ_P     (1<<2)
 #define TYPE_PREREQ_P  (1<<3)
 
-#define TYPE_REQ       (1<<4)
-#define TYPE_PREREQ    (1<<5)
+#define TYPE_REC       (1<<4)
+
+#define TYPE_REQ       (1<<5)
+#define TYPE_PREREQ    (1<<6)
 
 #define TYPE_CYCLETAIL  (1<<16)
 #define TYPE_CYCLEHEAD  (1<<17)
 
 #define TYPE_CYCLETAIL  (1<<16)
 #define TYPE_CYCLEHEAD  (1<<17)
@@ -245,19 +247,19 @@ addsolvableedges(struct orderdata *od, Solvable *s)
 {
   Transaction *trans = od->trans;
   Pool *pool = trans->pool;
 {
   Transaction *trans = od->trans;
   Pool *pool = trans->pool;
-  Id req, *reqp, con, *conp;
+  Id req, *reqp, con, *conp, rec, *recp;
   Id p, p2, pp2;
   int i, j, pre, numins;
   Repo *installed = pool->installed;
   Solvable *s2;
   Id p, p2, pp2;
   int i, j, pre, numins;
   Repo *installed = pool->installed;
   Solvable *s2;
-  Queue reqq;
+  Queue depq;
   int provbyinst;
 
 #if 0
   printf("addsolvableedges %s\n", pool_solvable2str(pool, s));
 #endif
   p = s - pool->solvables;
   int provbyinst;
 
 #if 0
   printf("addsolvableedges %s\n", pool_solvable2str(pool, s));
 #endif
   p = s - pool->solvables;
-  queue_init(&reqq);
+  queue_init(&depq);
   if (s->requires)
     {
       reqp = s->repo->idarraydata + s->requires;
   if (s->requires)
     {
       reqp = s->repo->idarraydata + s->requires;
@@ -269,15 +271,7 @@ addsolvableedges(struct orderdata *od, Solvable *s)
              pre = TYPE_PREREQ;
              continue;
            }
              pre = TYPE_PREREQ;
              continue;
            }
-#if 0
-         if (pre != TYPE_PREREQ && installed && s->repo == installed)
-           {
-             /* ignore normal requires if we're getting obsoleted */
-             if (trans->transaction_installed[p - pool->installed->start])
-               continue;
-           }
-#endif
-         queue_empty(&reqq);
+         queue_empty(&depq);
          numins = 0;   /* number of packages to be installed providing it */
          provbyinst = 0;       /* provided by kept package */
          FOR_PROVIDES(p2, pp2, req)
          numins = 0;   /* number of packages to be installed providing it */
          provbyinst = 0;       /* provided by kept package */
          FOR_PROVIDES(p2, pp2, req)
@@ -285,19 +279,13 @@ addsolvableedges(struct orderdata *od, Solvable *s)
              s2 = pool->solvables + p2;
              if (p2 == p)
                {
              s2 = pool->solvables + p2;
              if (p2 == p)
                {
-                 reqq.count = 0;       /* self provides */
+                 depq.count = 0;       /* self provides */
                  break;
                }
              if (s2->repo == installed && !MAPTST(&trans->transactsmap, p2))
                {
                  provbyinst = 1;
                  break;
                }
              if (s2->repo == installed && !MAPTST(&trans->transactsmap, p2))
                {
                  provbyinst = 1;
-#if 0
-                 printf("IGNORE inst provides %s by %s\n", pool_dep2str(pool, req), pool_solvable2str(pool, s2));
-                 reqq.count = 0;       /* provided by package that stays installed */
-                 break;
-#else
                  continue;
                  continue;
-#endif
                }
              if (s2->repo != installed && !MAPTST(&trans->transactsmap, p2))
                continue;               /* package stays uninstalled */
                }
              if (s2->repo != installed && !MAPTST(&trans->transactsmap, p2))
                continue;               /* package stays uninstalled */
@@ -305,7 +293,7 @@ addsolvableedges(struct orderdata *od, Solvable *s)
              if (s->repo == installed)
                {
                  /* s gets uninstalled */
              if (s->repo == installed)
                {
                  /* s gets uninstalled */
-                 queue_pushunique(&reqq, p2);
+                 queue_pushunique(&depq, p2);
                  if (s2->repo != installed)
                    numins++;
                }
                  if (s2->repo != installed)
                    numins++;
                }
@@ -313,55 +301,53 @@ addsolvableedges(struct orderdata *od, Solvable *s)
                {
                  if (s2->repo == installed)
                    continue;   /* s2 gets uninstalled */
                {
                  if (s2->repo == installed)
                    continue;   /* s2 gets uninstalled */
-                 queue_pushunique(&reqq, p2);
+                 queue_pushunique(&depq, p2);
                }
            }
          if (provbyinst)
            {
              /* prune to harmless ->inst edges */
                }
            }
          if (provbyinst)
            {
              /* prune to harmless ->inst edges */
-             for (i = j = 0; i < reqq.count; i++)
-               if (pool->solvables[reqq.elements[i]].repo != installed)
-                 reqq.elements[j++] = reqq.elements[i];
-             reqq.count = j;
+             for (i = j = 0; i < depq.count; i++)
+               if (pool->solvables[depq.elements[i]].repo != installed)
+                 depq.elements[j++] = depq.elements[i];
+             depq.count = j;
            }
 
            }
 
-         if (numins && reqq.count)
+         if (numins && depq.count)
            {
              if (s->repo == installed)
                {
            {
              if (s->repo == installed)
                {
-                 for (i = 0; i < reqq.count; i++)
+                 for (i = 0; i < depq.count; i++)
                    {
                    {
-                     if (pool->solvables[reqq.elements[i]].repo == installed)
+                     if (pool->solvables[depq.elements[i]].repo == installed)
                        {
                        {
-                         for (j = 0; j < reqq.count; j++)
+                         for (j = 0; j < depq.count; j++)
                            {
                            {
-                             if (pool->solvables[reqq.elements[j]].repo != installed)
+                             if (pool->solvables[depq.elements[j]].repo != installed)
                                {
                                {
-                                 if (trans->transaction_installed[reqq.elements[i] - pool->installed->start] == reqq.elements[j])
+                                 if (trans->transaction_installed[depq.elements[i] - pool->installed->start] == depq.elements[j])
                                    continue;   /* no self edge */
 #if 0
                                    continue;   /* no self edge */
 #if 0
-                                 printf("add interrreq uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, reqq.elements[i]), pool_dep2str(pool, req), pool_solvid2str(pool, reqq.elements[j]));
+                                 printf("add interrreq uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, depq.elements[i]), pool_dep2str(pool, req), pool_solvid2str(pool, depq.elements[j]));
 #endif
 #endif
-                                 addedge(od, reqq.elements[i], reqq.elements[j], pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
+                                 addedge(od, depq.elements[i], depq.elements[j], pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
                                }
                            }
                        }
                    }
                }
              /* no mixed types, remove all deps on uninstalls */
                                }
                            }
                        }
                    }
                }
              /* no mixed types, remove all deps on uninstalls */
-             for (i = j = 0; i < reqq.count; i++)
-               if (pool->solvables[reqq.elements[i]].repo != installed)
-                 reqq.elements[j++] = reqq.elements[i];
-             reqq.count = j;
+             for (i = j = 0; i < depq.count; i++)
+               if (pool->solvables[depq.elements[i]].repo != installed)
+                 depq.elements[j++] = depq.elements[i];
+             depq.count = j;
            }
            }
-          if (!reqq.count)
-           continue;
-          for (i = 0; i < reqq.count; i++)
+          for (i = 0; i < depq.count; i++)
            {
            {
-             p2 = reqq.elements[i];
+             p2 = depq.elements[i];
              if (pool->solvables[p2].repo != installed)
                {
              if (pool->solvables[p2].repo != installed)
                {
-                 /* all elements of reqq are installs, thus have different TEs */
+                 /* all elements of depq are installs, thus have different TEs */
                  if (pool->solvables[p].repo != installed)
                    {
 #if 0
                  if (pool->solvables[p].repo != installed)
                    {
 #if 0
@@ -435,13 +421,47 @@ addsolvableedges(struct orderdata *od, Solvable *s)
            }
        }
     }
            }
        }
     }
-  if (s->repo == installed && solvable_lookup_idarray(s, SOLVABLE_TRIGGERS, &reqq) && reqq.count)
+  if (s->recommends && s->repo != installed)
+    {
+      recp = s->repo->idarraydata + s->recommends;
+      while ((rec = *recp++) != 0)
+       {
+         queue_empty(&depq);
+         FOR_PROVIDES(p2, pp2, rec)
+           {
+             s2 = pool->solvables + p2;
+             if (p2 == p)
+               {
+                 depq.count = 0;       /* self provides */
+                 break;
+               }
+             if (s2->repo == installed && !MAPTST(&trans->transactsmap, p2))
+               continue;
+             if (s2->repo != installed && !MAPTST(&trans->transactsmap, p2))
+               continue;               /* package stays uninstalled */
+             if (s2->repo != installed)
+               queue_pushunique(&depq, p2);
+           }
+          for (i = 0; i < depq.count; i++)
+           {
+             p2 = depq.elements[i];
+             if (pool->solvables[p2].repo != installed)
+               {
+#if 0
+                 printf("add recommends inst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, rec), pool_solvid2str(pool, p2));
+#endif
+                 addedge(od, p, p2, TYPE_REC);
+               }
+           }
+       }
+    }
+  if (s->repo == installed && solvable_lookup_idarray(s, SOLVABLE_TRIGGERS, &depq) && depq.count)
     {
       /* we're getting deinstalled/updated. Try to do this before our
        * triggers are hit */
     {
       /* we're getting deinstalled/updated. Try to do this before our
        * triggers are hit */
-      for (i = 0; i < reqq.count; i++)
+      for (i = 0; i < depq.count; i++)
        {
        {
-         Id tri = reqq.elements[i];
+         Id tri = depq.elements[i];
          FOR_PROVIDES(p2, pp2, tri)
            {
              if (p2 == p)
          FOR_PROVIDES(p2, pp2, tri)
            {
              if (p2 == p)
@@ -462,7 +482,7 @@ addsolvableedges(struct orderdata *od, Solvable *s)
            }
        }
     }
            }
        }
     }
-  queue_free(&reqq);
+  queue_free(&depq);
 }
 
 
 }
 
 
index 468be67..6f06101 100644 (file)
@@ -145,7 +145,7 @@ solver_prune_installed_dup_packages(Solver *solv, Queue *plist)
       Solvable *s = pool->solvables + p;
       if (s->repo != pool->installed && s->repo->priority < bestprio)
        continue;
       Solvable *s = pool->solvables + p;
       if (s->repo != pool->installed && s->repo->priority < bestprio)
        continue;
-      if (s->repo == pool->installed && (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))))
+      if (s->repo == pool->installed && (solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))))
        {
          Id p2, pp2;
          int keepit = 0;
        {
          Id p2, pp2;
          int keepit = 0;
@@ -181,7 +181,7 @@ static inline void
 solver_prune_to_highest_prio(Solver *solv, Queue *plist)
 {
   prune_to_highest_prio(solv->pool, plist);
 solver_prune_to_highest_prio(Solver *solv, Queue *plist)
 {
   prune_to_highest_prio(solv->pool, plist);
-  if (plist->count > 1 && solv->pool->installed && (solv->dupmap_all || solv->dupinvolvedmap.size))
+  if (plist->count > 1 && solv->pool->installed && (solv->dupinvolvedmap_all || solv->dupinvolvedmap.size))
     solver_prune_installed_dup_packages(solv, plist);
 }
 
     solver_prune_installed_dup_packages(solv, plist);
 }
 
@@ -1424,7 +1424,7 @@ policy_is_illegal(Solver *solv, Solvable *is, Solvable *s, int ignore)
 {
   Pool *pool = solv->pool;
   int ret = 0;
 {
   Pool *pool = solv->pool;
   int ret = 0;
-  int duppkg = solv->dupmap_all ? 1 : 0;
+  int duppkg = solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, is - pool->solvables));
   if (!(ignore & POLICY_ILLEGAL_DOWNGRADE) && !(duppkg ? solv->dup_allowdowngrade : solv->allowdowngrade))
     {
       if (is->name == s->name && pool_evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE) > 0)
   if (!(ignore & POLICY_ILLEGAL_DOWNGRADE) && !(duppkg ? solv->dup_allowdowngrade : solv->allowdowngrade))
     {
       if (is->name == s->name && pool_evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE) > 0)
index a9bbdce..2ffb6f9 100644 (file)
@@ -454,13 +454,6 @@ convertsolution(Solver *solv, Id why, Queue *solutionq)
          return;       /* false alarm */
 
       p = solv->installed->start + (why - solv->updaterules);
          return;       /* false alarm */
 
       p = solv->installed->start + (why - solv->updaterules);
-      if (solv->dupmap_all && solv->rules[why].p != p && solv->decisionmap[p] > 0)
-       {
-         /* distupgrade case, allow to keep old package */
-         queue_push(solutionq, SOLVER_SOLUTION_DISTUPGRADE);
-         queue_push(solutionq, p);
-         return;
-       }
       if (solv->decisionmap[p] > 0)
        return;         /* false alarm, turned out we can keep the package */
       rr = solv->rules + solv->featurerules + (why - solv->updaterules);
       if (solv->decisionmap[p] > 0)
        return;         /* false alarm, turned out we can keep the package */
       rr = solv->rules + solv->featurerules + (why - solv->updaterules);
index 5654330..b5f3e3e 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2007-2009, Novell Inc.
+ * Copyright (c) 2007-2017, SUSE Inc.
  *
  * This program is licensed under the BSD license, read LICENSE.BSD
  * for further information
  *
  * This program is licensed under the BSD license, read LICENSE.BSD
  * for further information
 #define RULES_BLOCK 63
 
 static void addpkgruleinfo(Solver *solv, Id p, Id p2, Id d, int type, Id dep);
 #define RULES_BLOCK 63
 
 static void addpkgruleinfo(Solver *solv, Id p, Id p2, Id d, int type, Id dep);
-static void solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded);
-
-/*-------------------------------------------------------------------
- * Check if dependency is possible
- *
- * mirrors solver_dep_fulfilled but uses map m instead of the decisionmap.
- * used in solver_addpkgrulesforweak and solver_createcleandepsmap.
- */
-
-static inline int
-dep_possible(Solver *solv, Id dep, Map *m)
-{
-  Pool *pool = solv->pool;
-  Id p, pp;
-
-  if (ISRELDEP(dep))
-    {
-      Reldep *rd = GETRELDEP(pool, dep);
-      if (rd->flags >= 8)
-        {
-         if (rd->flags == REL_COND || rd->flags == REL_UNLESS)
-           return 1;
-         if (rd->flags == REL_AND)
-           {
-             if (!dep_possible(solv, rd->name, m))
-               return 0;
-             return dep_possible(solv, rd->evr, m);
-           }
-         if (rd->flags == REL_OR)
-           {
-             if (dep_possible(solv, rd->name, m))
-               return 1;
-             return dep_possible(solv, rd->evr, m);
-           }
-         if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
-           return solver_splitprovides(solv, rd->evr, m);
-       }
-    }
-  FOR_PROVIDES(p, pp, dep)
-    {
-      if (MAPTST(m, p))
-       return 1;
-    }
-  return 0;
-}
 
 static inline int
 is_otherproviders_dep(Pool *pool, Id dep)
 
 static inline int
 is_otherproviders_dep(Pool *pool, Id dep)
@@ -1205,7 +1160,7 @@ solver_addpkgrulesforweak(Solver *solv, Map *m)
          /* find possible supplements */
          supp = s->repo->idarraydata + s->supplements;
          while ((sup = *supp++) != 0)
          /* find possible supplements */
          supp = s->repo->idarraydata + s->supplements;
          while ((sup = *supp++) != 0)
-           if (dep_possible(solv, sup, m))
+           if (solver_dep_possible(solv, sup, m))
              break;
        }
 
              break;
        }
 
@@ -1214,7 +1169,7 @@ solver_addpkgrulesforweak(Solver *solv, Map *m)
        {
          supp = s->repo->idarraydata + s->enhances;
          while ((sup = *supp++) != 0)
        {
          supp = s->repo->idarraydata + s->enhances;
          while ((sup = *supp++) != 0)
-           if (dep_possible(solv, sup, m))
+           if (solver_dep_possible(solv, sup, m))
              break;
        }
       /* if nothing found, goto next solvables */
              break;
        }
       /* if nothing found, goto next solvables */
@@ -1294,58 +1249,6 @@ dup_maykeepinstalled(Solver *solv, Solvable *s)
 }
 
 
 }
 
 
-static Id
-finddistupgradepackages(Solver *solv, Solvable *s, Queue *qs)
-{
-  Pool *pool = solv->pool;
-  int i, j;
-
-  policy_findupdatepackages(solv, s, qs, 2);
-  if (qs->count)
-    {
-      /* remove installed packages we can't keep */
-      for (i = j = 0; i < qs->count; i++)
-       {
-         Solvable *ns = pool->solvables + qs->elements[i];
-         if (ns->repo == pool->installed && !dup_maykeepinstalled(solv, ns))
-           continue;
-         qs->elements[j++] = qs->elements[i];
-       }
-      queue_truncate(qs, j);
-    }
-  /* check if it is ok to keep the installed package */
-  if (dup_maykeepinstalled(solv, s))
-    return s - pool->solvables;
-  /* nope, it must be some other package */
-  return -SYSTEMSOLVABLE;
-}
-
-#if 0
-/* add packages from the dup repositories to the update candidates
- * this isn't needed for the global dup mode as all packages are
- * from dup repos in that case */
-static void
-addduppackages(Solver *solv, Solvable *s, Queue *qs)
-{
-  Queue dupqs;
-  Id p, dupqsbuf[64];
-  int i;
-  int oldnoupdateprovide = solv->noupdateprovide;
-
-  queue_init_buffer(&dupqs, dupqsbuf, sizeof(dupqsbuf)/sizeof(*dupqsbuf));
-  solv->noupdateprovide = 1;
-  policy_findupdatepackages(solv, s, &dupqs, 2);
-  solv->noupdateprovide = oldnoupdateprovide;
-  for (i = 0; i < dupqs.count; i++)
-    {
-      p = dupqs.elements[i];
-      if (MAPTST(&solv->dupmap, p))
-        queue_pushunique(qs, p);
-    }
-  queue_free(&dupqs);
-}
-#endif
-
 /* stash away the original updaters for multiversion packages. We do this so that
  * we can update the package later */
 static inline void
 /* stash away the original updaters for multiversion packages. We do this so that
  * we can update the package later */
 static inline void
@@ -1392,7 +1295,7 @@ solver_addfeaturerule(Solver *solv, Solvable *s)
   queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
   p = s - pool->solvables;
   policy_findupdatepackages(solv, s, &qs, 1);
   queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
   p = s - pool->solvables;
   policy_findupdatepackages(solv, s, &qs, 1);
-  if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
+  if (solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
     {
       if (!dup_maykeepinstalled(solv, s))
        {
     {
       if (!dup_maykeepinstalled(solv, s))
        {
@@ -1440,8 +1343,8 @@ solver_addupdaterule(Solver *solv, Solvable *s)
   Id p, d;
   Queue qs;
   Id qsbuf[64];
   Id p, d;
   Queue qs;
   Id qsbuf[64];
-  int isorphaned = 0;
   Rule *r;
   Rule *r;
+  int dupinvolved = 0;
 
   p = s - pool->solvables;
   /* Orphan detection. We cheat by looking at the feature rule, which
 
   p = s - pool->solvables;
   /* Orphan detection. We cheat by looking at the feature rule, which
@@ -1464,12 +1367,10 @@ solver_addupdaterule(Solver *solv, Solvable *s)
       return;
     }
 
       return;
     }
 
-  queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
   /* find update candidates for 's' */
   /* find update candidates for 's' */
-  if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
-    p = finddistupgradepackages(solv, s, &qs);
-  else
-    policy_findupdatepackages(solv, s, &qs, 0);
+  queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
+  dupinvolved = solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p));
+  policy_findupdatepackages(solv, s, &qs, dupinvolved ? 2 : 0);
 
   if (qs.count && solv->multiversion.size)
     {
 
   if (qs.count && solv->multiversion.size)
     {
@@ -1500,44 +1401,41 @@ solver_addupdaterule(Solver *solv, Solvable *s)
                }
              qs.elements[j++] = qs.elements[i];
            }
                }
              qs.elements[j++] = qs.elements[i];
            }
-         if (j < qs.count)             /* filtered at least one package? */
+
+         if (j == 0 && dupinvolved && !dup_maykeepinstalled(solv, s))
            {
            {
-             if (j == 0 && p == -SYSTEMSOLVABLE)
+             /* this is a multiversion orphan */
+             queue_push(&solv->orphaned, p);
+             set_specialupdaters(solv, s, d);
+             if (solv->keep_orphans && !(solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start))))
                {
                {
-                 /* this is a multiversion orphan */
-                 queue_push(&solv->orphaned, s - pool->solvables);
-                 set_specialupdaters(solv, s, d);
-                 if (solv->keep_orphans && !(solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, s - pool->solvables - solv->installed->start))))
-                   {
-                     /* we need to keep the orphan */
-                     queue_free(&qs);
-                     solver_addrule(solv, s - pool->solvables, 0, 0);
-                     return;
-                   }
-                 /* we can drop it as long as we update */
-                 isorphaned = 1;
-                 j = qs.count;         /* force the update */
+                 /* we need to keep the orphan */
+                 queue_free(&qs);
+                 solver_addrule(solv, p, 0, 0);
+                 return;
                }
                }
-             else if (d && (solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, s - pool->solvables - solv->installed->start))))
+             /* we can drop it as long as we update */
+             j = qs.count;
+           }
+
+         if (j < qs.count)             /* filtered at least one package? */
+           {
+             if (d && (solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, p - solv->installed->start))))
                {
                  /* non-orphan multiversion package, set special updaters if we want an update */
                  set_specialupdaters(solv, s, d);
                }
              qs.count = j;
            }
                {
                  /* non-orphan multiversion package, set special updaters if we want an update */
                  set_specialupdaters(solv, s, d);
                }
              qs.count = j;
            }
-         else if (p != -SYSTEMSOLVABLE)
+         else
            {
              /* could fallthrough, but then we would do pool_queuetowhatprovides twice */
              queue_free(&qs);
            {
              /* could fallthrough, but then we would do pool_queuetowhatprovides twice */
              queue_free(&qs);
-             solver_addrule(solv, s - pool->solvables, 0, d);  /* allow update of s */
+             solver_addrule(solv, p, 0, d);    /* allow update of s */
              return;
            }
        }
     }
              return;
            }
        }
     }
-  if (!isorphaned && p == -SYSTEMSOLVABLE && qs.count && solv->dupmap.size)
-    p = s - pool->solvables;           /* let the dup rules sort it out */
-  if (qs.count && p == -SYSTEMSOLVABLE)
-    p = queue_shift(&qs);
   if (qs.count > 1)
     {
       d = pool_queuetowhatprovides(pool, &qs);
   if (qs.count > 1)
     {
       d = pool_queuetowhatprovides(pool, &qs);
@@ -1664,9 +1562,10 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
          a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
          if (a != 1 && installed && ps->repo == installed)
            {
          a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
          if (a != 1 && installed && ps->repo == installed)
            {
-             if (!solv->dupmap_all && !(solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
-               queue_pushunique(&allowedarchs, ps->arch);      /* also ok to keep this architecture */
-             continue;         /* ignore installed solvables when calculating the best arch */
+             if (solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
+               continue;
+             queue_pushunique(&allowedarchs, ps->arch);        /* also ok to keep this architecture */
+             continue;         /* but ignore installed solvables when calculating the best arch */
            }
          if (a && a != 1 && (!bestarch || a < bestarch))
            {
            }
          if (a && a != 1 && (!bestarch || a < bestarch))
            {
@@ -1692,7 +1591,7 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
              ps = pool->solvables + p;
              if (ps->name != s->name || ps->repo != installed || !MAPTST(addedmap, p))
                continue;
              ps = pool->solvables + p;
              if (ps->name != s->name || ps->repo != installed || !MAPTST(addedmap, p))
                continue;
-             if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
+             if (solv->dupinvolvedmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
                continue;
              a = ps->arch;
              a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
                continue;
              a = ps->arch;
              a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
@@ -1834,7 +1733,7 @@ reenableinfarchrule(Solver *solv, Id name)
  ***/
 
 static inline void
  ***/
 
 static inline void
-add_cleandeps_package(Solver *solv, Id p)
+add_cleandeps_updatepkg(Solver *solv, Id p)
 {
   if (!solv->cleandeps_updatepkgs)
     {
 {
   if (!solv->cleandeps_updatepkgs)
     {
@@ -1852,6 +1751,9 @@ solver_addtodupmaps(Solver *solv, Id p, Id how, int targeted)
   Repo *installed = solv->installed;
   Id pi, pip, obs, *obsp;
 
   Repo *installed = solv->installed;
   Id pi, pip, obs, *obsp;
 
+  if (!solv->dupinvolvedmap.size)
+    map_grow(&solv->dupinvolvedmap, pool->nsolvables);
+
   MAPSET(&solv->dupinvolvedmap, p);
   if (targeted)
     MAPSET(&solv->dupmap, p);
   MAPSET(&solv->dupinvolvedmap, p);
   if (targeted)
     MAPSET(&solv->dupmap, p);
@@ -1868,14 +1770,14 @@ solver_addtodupmaps(Solver *solv, Id p, Id how, int targeted)
            if (pool->solvables[pi2].repo != installed)
              MAPSET(&solv->dupinvolvedmap, pi2);
        }
            if (pool->solvables[pi2].repo != installed)
              MAPSET(&solv->dupinvolvedmap, pi2);
        }
-      if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0)
+      if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0 && !solv->bestupdatemap_all)
        {
          if (!solv->bestupdatemap.size)
            map_grow(&solv->bestupdatemap, installed->end - installed->start);
          MAPSET(&solv->bestupdatemap, pi - installed->start);
        }
       if (ps->repo == installed && (how & SOLVER_CLEANDEPS) != 0)
        {
          if (!solv->bestupdatemap.size)
            map_grow(&solv->bestupdatemap, installed->end - installed->start);
          MAPSET(&solv->bestupdatemap, pi - installed->start);
        }
       if (ps->repo == installed && (how & SOLVER_CLEANDEPS) != 0)
-       add_cleandeps_package(solv, pi);
+       add_cleandeps_updatepkg(solv, pi);
       if (!targeted && ps->repo != installed)
        MAPSET(&solv->dupmap, pi);
     }
       if (!targeted && ps->repo != installed)
        MAPSET(&solv->dupmap, pi);
     }
@@ -1913,19 +1815,23 @@ solver_addtodupmaps(Solver *solv, Id p, Id how, int targeted)
                    if (pool->solvables[pi2].repo != installed)
                      MAPSET(&solv->dupinvolvedmap, pi2);
                }
                    if (pool->solvables[pi2].repo != installed)
                      MAPSET(&solv->dupinvolvedmap, pi2);
                }
-             if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0)
+             if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0 && !solv->bestupdatemap_all)
                {
                  if (!solv->bestupdatemap.size)
                    map_grow(&solv->bestupdatemap, installed->end - installed->start);
                  MAPSET(&solv->bestupdatemap, pi - installed->start);
                }
              if (ps->repo == installed && (how & SOLVER_CLEANDEPS) != 0)
                {
                  if (!solv->bestupdatemap.size)
                    map_grow(&solv->bestupdatemap, installed->end - installed->start);
                  MAPSET(&solv->bestupdatemap, pi - installed->start);
                }
              if (ps->repo == installed && (how & SOLVER_CLEANDEPS) != 0)
-               add_cleandeps_package(solv, pi);
+               add_cleandeps_updatepkg(solv, pi);
            }
        }
     }
 }
 
            }
        }
     }
 }
 
+/* create the two dupmaps:
+ * - dupmap: packages in that map are good to install/keep
+ * - dupinvolvedmap: packages are subject to dup mode
+ */
 void
 solver_createdupmaps(Solver *solv)
 {
 void
 solver_createdupmaps(Solver *solv)
 {
@@ -1937,7 +1843,8 @@ solver_createdupmaps(Solver *solv)
   int i, targeted;
 
   map_init(&solv->dupmap, pool->nsolvables);
   int i, targeted;
 
   map_init(&solv->dupmap, pool->nsolvables);
-  map_init(&solv->dupinvolvedmap, pool->nsolvables);
+  solv->dupinvolvedmap_all = 0;
+  map_init(&solv->dupinvolvedmap, 0);
   for (i = 0; i < job->count; i += 2)
     {
       how = job->elements[i];
   for (i = 0; i < job->count; i += 2)
     {
       how = job->elements[i];
@@ -1966,11 +1873,22 @@ solver_createdupmaps(Solver *solv)
            }
          else if (select == SOLVER_SOLVABLE_ALL)
            {
            }
          else if (select == SOLVER_SOLVABLE_ALL)
            {
+             solv->dupinvolvedmap_all = 1;
              FOR_POOL_SOLVABLES(p)
                {
              FOR_POOL_SOLVABLES(p)
                {
-                 MAPSET(&solv->dupinvolvedmap, p);
-                 if (installed && pool->solvables[p].repo != installed)
-                   MAPSET(&solv->dupmap, p);
+                 Solvable *s = pool->solvables + p;
+                 if (!s->repo || s->repo == installed)
+                   continue;
+                 if (!pool_installable(pool, s))
+                   continue;
+                 MAPSET(&solv->dupmap, p);
+               }
+             if ((how & SOLVER_FORCEBEST) != 0)
+               solv->bestupdatemap_all = 1;
+             if ((how & SOLVER_CLEANDEPS) != 0 && installed)
+               {
+                 FOR_REPO_SOLVABLES(installed, p, s)
+                   add_cleandeps_updatepkg(solv, p);
                }
            }
          else
                }
            }
          else
@@ -2002,7 +1920,8 @@ solver_createdupmaps(Solver *solv)
          break;
        }
     }
          break;
        }
     }
-  MAPCLR(&solv->dupinvolvedmap, SYSTEMSOLVABLE);
+  if (solv->dupinvolvedmap.size)
+    MAPCLR(&solv->dupinvolvedmap, SYSTEMSOLVABLE);
 }
 
 void
 }
 
 void
@@ -2024,6 +1943,8 @@ solver_addduprules(Solver *solv, Map *addedmap)
   Rule *r;
 
   solv->duprules = solv->nrules;
   Rule *r;
 
   solv->duprules = solv->nrules;
+  if (solv->dupinvolvedmap_all)
+    solv->updatemap_all = 1;
   for (i = 1; i < pool->nsolvables; i++)
     {
       if (i == SYSTEMSOLVABLE || !MAPTST(addedmap, i))
   for (i = 1; i < pool->nsolvables; i++)
     {
       if (i == SYSTEMSOLVABLE || !MAPTST(addedmap, i))
@@ -2039,13 +1960,16 @@ solver_addduprules(Solver *solv, Map *addedmap)
            first = 0;
          if (first)
            break;
            first = 0;
          if (first)
            break;
-         if (!MAPTST(&solv->dupinvolvedmap, p))
+         if (!solv->dupinvolvedmap_all && !MAPTST(&solv->dupinvolvedmap, p))
            continue;
          if (installed && ps->repo == installed)
            {
            continue;
          if (installed && ps->repo == installed)
            {
-             if (!solv->updatemap.size)
-               map_grow(&solv->updatemap, installed->end - installed->start);
-             MAPSET(&solv->updatemap, p - installed->start);
+             if (!solv->updatemap_all)
+               {
+                 if (!solv->updatemap.size)
+                   map_grow(&solv->updatemap, installed->end - installed->start);
+                 MAPSET(&solv->updatemap, p - installed->start);
+               }
              if (!MAPTST(&solv->dupmap, p))
                {
                  Id ip, ipp;
              if (!MAPTST(&solv->dupmap, p))
                {
                  Id ip, ipp;
@@ -2060,24 +1984,35 @@ solver_addduprules(Solver *solv, Map *addedmap)
                    }
                  if (ip)
                    {
                    }
                  if (ip)
                    {
-                     /* ok, found a good one. we may keep this package. */
+                     /* ok, identical to a good one. we may keep this package. */
                      MAPSET(&solv->dupmap, p);         /* for best rules processing */
                      continue;
                    }
                      MAPSET(&solv->dupmap, p);         /* for best rules processing */
                      continue;
                    }
+                 /* check if it's orphaned. If yes, we may keep it */
                  r = solv->rules + solv->updaterules + (p - installed->start);
                  if (!r->p)
                  r = solv->rules + solv->updaterules + (p - installed->start);
                  if (!r->p)
-                     r = solv->rules + solv->featurerules + (p - installed->start);
-                 if (r->p && solv->specialupdaters && solv->specialupdaters[p - installed->start])
+                   r = solv->rules + solv->featurerules + (p - installed->start);
+                 if (!r->p)
+                   {
+                     /* no update/feature rule, this is an orphan */
+                     MAPSET(&solv->dupmap, p);         /* for best rules processing */
+                     continue;
+                   }
+                 if (solv->specialupdaters && solv->specialupdaters[p - installed->start])
                    {
                      /* this is a multiversion orphan, we're good if an update is installed */
                      solver_addrule(solv, -p, 0, solv->specialupdaters[p - installed->start]);
                      continue;
                    }
                    {
                      /* this is a multiversion orphan, we're good if an update is installed */
                      solver_addrule(solv, -p, 0, solv->specialupdaters[p - installed->start]);
                      continue;
                    }
-                 if (!r->p || (r->p == p && !r->d && !r->w2))
+                 if (r->p == p && !r->d && !r->w2)
                    {
                    {
-                     /* this is an orphan */
-                     MAPSET(&solv->dupmap, p);         /* for best rules processing */
-                     continue;
+                     r = solv->rules + solv->featurerules + (p - installed->start);
+                     if (!r->p || (!r->d && !r->w2))
+                       {
+                         /* this is an orphan */
+                         MAPSET(&solv->dupmap, p);             /* for best rules processing */
+                         continue;
+                       }
                    }
                  solver_addrule(solv, -p, 0, 0);       /* no match, sorry */
                }
                    }
                  solver_addrule(solv, -p, 0, 0);       /* no match, sorry */
                }
@@ -2137,120 +2072,6 @@ reenableduprule(Solver *solv, Id name)
 #define DISABLE_INFARCH        2
 #define DISABLE_DUP    3
 
 #define DISABLE_INFARCH        2
 #define DISABLE_DUP    3
 
-/*
- * add all installed packages that package p obsoletes to Queue q.
- * Package p is not installed. Also, we know that if
- * solv->keepexplicitobsoletes is not set, p is not in the multiversion map.
- * Entries may get added multiple times.
- */
-static void
-add_obsoletes(Solver *solv, Id p, Queue *q)
-{
-  Pool *pool = solv->pool;
-  Repo *installed = solv->installed;
-  Id p2, pp2;
-  Solvable *s = pool->solvables + p;
-  Id obs, *obsp;
-  Id lastp2 = 0;
-
-  if (!solv->keepexplicitobsoletes || !(solv->multiversion.size && MAPTST(&solv->multiversion, p)))
-    {
-      FOR_PROVIDES(p2, pp2, s->name)
-       {
-         Solvable *ps = pool->solvables + p2;
-         if (ps->repo != installed)
-           continue;
-         if (!pool->implicitobsoleteusesprovides && ps->name != s->name)
-           continue;
-         if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
-           continue;
-         queue_push(q, p2);
-         lastp2 = p2;
-       }
-    }
-  if (!s->obsoletes)
-    return;
-  obsp = s->repo->idarraydata + s->obsoletes;
-  while ((obs = *obsp++) != 0)
-    FOR_PROVIDES(p2, pp2, obs)
-      {
-       Solvable *ps = pool->solvables + p2;
-       if (ps->repo != installed)
-         continue;
-       if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
-         continue;
-       if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
-         continue;
-       if (p2 == lastp2)
-         continue;
-       queue_push(q, p2);
-       lastp2 = p2;
-      }
-}
-
-/*
- * Call add_obsoletes and intersect the result with the
- * elements in Queue q starting at qstart.
- * Assumes that it's the first call if qstart == q->count.
- * May use auxillary map m for the intersection process, all
- * elements of q starting at qstart must have their bit cleared.
- * (This is also true after the function returns.)
- */
-static void
-intersect_obsoletes(Solver *solv, Id p, Queue *q, int qstart, Map *m)
-{
-  int i, j;
-  int qcount = q->count;
-
-  add_obsoletes(solv, p, q);
-  if (qcount == qstart)
-    return;    /* first call */
-  if (qcount == q->count)
-    j = qstart;        
-  else if (qcount == qstart + 1)
-    {
-      /* easy if there's just one element */
-      j = qstart;
-      for (i = qcount; i < q->count; i++)
-       if (q->elements[i] == q->elements[qstart])
-         {
-           j++;        /* keep the element */
-           break;
-         }
-    }
-  else if (!m->size && q->count - qstart <= 8)
-    {
-      /* faster than a map most of the time */
-      int k;
-      for (i = j = qstart; i < qcount; i++)
-       {
-         Id ip = q->elements[i];
-         for (k = qcount; k < q->count; k++)
-           if (q->elements[k] == ip)
-             {
-               q->elements[j++] = ip;
-               break;
-             }
-       }
-    }
-  else
-    {
-      /* for the really pathologic cases we use the map */
-      Repo *installed = solv->installed;
-      if (!m->size)
-       map_init(m, installed->end - installed->start);
-      for (i = qcount; i < q->count; i++)
-       MAPSET(m, q->elements[i] - installed->start);
-      for (i = j = qstart; i < qcount; i++)
-       if (MAPTST(m, q->elements[i] - installed->start))
-         {
-           MAPCLR(m, q->elements[i] - installed->start);
-           q->elements[j++] = q->elements[i];
-         }
-    }
-  queue_truncate(q, j);
-}
-
 static void
 jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
 {
 static void
 jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
 {
@@ -2343,30 +2164,19 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
        return;
       /* now the hard part: disable some update rules */
 
        return;
       /* now the hard part: disable some update rules */
 
-      /* first check if we have multiversion or installed packages in the job */
-      i = j = 0;
+      /* first check if we have installed or multiversion packages in the job */
       FOR_JOB_SELECT(p, pp, select, what)
        {
          if (pool->solvables[p].repo == installed)
       FOR_JOB_SELECT(p, pp, select, what)
        {
          if (pool->solvables[p].repo == installed)
-           j = p;
-         else if (solv->multiversion.size && MAPTST(&solv->multiversion, p) && !solv->keepexplicitobsoletes)
            return;
            return;
-         i++;
-       }
-      if (j)   /* have installed packages */
-       {
-         /* this is for dupmap_all jobs, it can go away if we create
-          * duprules for them */
-         if (i == 1 && (set & SOLVER_SETREPO) != 0)
-           queue_push2(q, DISABLE_UPDATE, j);
-         return;
+         if (solv->multiversion.size && MAPTST(&solv->multiversion, p) && !solv->keepexplicitobsoletes)
+           return;
        }
        }
-
       omap.size = 0;
       qstart = q->count;
       FOR_JOB_SELECT(p, pp, select, what)
        {
       omap.size = 0;
       qstart = q->count;
       FOR_JOB_SELECT(p, pp, select, what)
        {
-         intersect_obsoletes(solv, p, q, qstart, &omap);
+         solver_intersect_obsoleted(solv, p, q, qstart, &omap);
          if (q->count == qstart)
            break;
        }
          if (q->count == qstart)
            break;
        }
@@ -3439,11 +3249,6 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
   int i, oldcnt;
 
   solv->bestrules = solv->nrules;
   int i, oldcnt;
 
   solv->bestrules = solv->nrules;
-  if (!installed)
-    {
-      solv->bestrules_end = solv->nrules;
-      return;
-    }
   queue_init(&q);
   queue_init(&q2);
   queue_init(&r2pkg);
   queue_init(&q);
   queue_init(&q2);
   queue_init(&r2pkg);
@@ -3483,7 +3288,7 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
        }
     }
 
        }
     }
 
-  if (solv->bestupdatemap_all || solv->bestupdatemap.size)
+  if (installed && (solv->bestupdatemap_all || solv->bestupdatemap.size))
     {
       Map m;
 
     {
       Map m;
 
@@ -3810,1125 +3615,6 @@ for (j = 0; j < qq.count; j++)
   POOL_DEBUG(SOLV_DEBUG_STATS, "yumobs rule creation took %d ms\n", solv_timems(now));
 }
 
   POOL_DEBUG(SOLV_DEBUG_STATS, "yumobs rule creation took %d ms\n", solv_timems(now));
 }
 
-#undef CLEANDEPSDEBUG
-
-/*
- * This functions collects all packages that are looked at
- * when a dependency is checked. We need it to "pin" installed
- * packages when removing a supplemented package in createcleandepsmap.
- * Here's an not uncommon example:
- *   A contains "Supplements: packageand(B, C)"
- *   B contains "Requires: A"
- * Now if we remove C, the supplements is no longer true,
- * thus we also remove A. Without the dep_pkgcheck function, we
- * would now also remove B, but this is wrong, as adding back
- * C doesn't make the supplements true again. Thus we "pin" B
- * when we remove A.
- * There's probably a better way to do this, but I haven't come
- * up with it yet ;)
- */
-static inline void
-dep_pkgcheck(Solver *solv, Id dep, Map *m, Queue *q)
-{
-  Pool *pool = solv->pool;
-  Id p, pp;
-
-  if (ISRELDEP(dep))
-    {
-      Reldep *rd = GETRELDEP(pool, dep);
-      if (rd->flags >= 8)
-       {
-         if (rd->flags == REL_AND)
-           {
-             dep_pkgcheck(solv, rd->name, m, q);
-             dep_pkgcheck(solv, rd->evr, m, q);
-             return;
-           }
-         if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
-           return;
-       }
-    }
-  FOR_PROVIDES(p, pp, dep)
-    if (!m || MAPTST(m, p))
-      queue_push(q, p);
-}
-
-static int
-check_xsupp(Solver *solv, Queue *depq, Id dep)
-{
-  Pool *pool = solv->pool;
-  Id p, pp;
-
-  if (ISRELDEP(dep))
-    {
-      Reldep *rd = GETRELDEP(pool, dep);
-      if (rd->flags >= 8)
-       {
-         if (rd->flags == REL_AND)
-           {
-             if (!check_xsupp(solv, depq, rd->name))
-               return 0;
-             return check_xsupp(solv, depq, rd->evr);
-           }
-         if (rd->flags == REL_OR)
-           {
-             if (check_xsupp(solv, depq, rd->name))
-               return 1;
-             return check_xsupp(solv, depq, rd->evr);
-           }
-         if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
-#if 0
-           return solver_splitprovides(solv, rd->evr);
-#else
-           return 0;
-#endif
-       }
-      if (depq && rd->flags == REL_NAMESPACE)
-       {
-         int i;
-         for (i = 0; i < depq->count; i++)
-           if (depq->elements[i] == dep || depq->elements[i] == rd->name)
-            return 1;
-       }
-    }
-  FOR_PROVIDES(p, pp, dep)
-    if (p == SYSTEMSOLVABLE || pool->solvables[p].repo == solv->installed)
-      return 1;
-  return 0;
-}
-
-static inline int
-queue_contains(Queue *q, Id id)
-{
-  int i;
-  for (i = 0; i < q->count; i++)
-    if (q->elements[i] == id)
-      return 1;
-  return 0;
-}
-
-#ifdef ENABLE_COMPLEX_DEPS
-static void
-complex_cleandeps_remove(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq)
-{
-  int i;
-  Queue dq;
-  Id p;
-
-  queue_init(&dq);
-  i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
-  if (i == 0 || i == 1)
-    {
-      queue_free(&dq);
-      return;
-    }
-  for (i = 0; i < dq.count; i++)
-    {
-      for (; (p = dq.elements[i]) != 0; i++)
-       {
-         if (p < 0)
-           {
-             if (!MAPTST(installedm, -p))
-               break;
-             continue;
-           }
-         if (p != SYSTEMSOLVABLE && MAPTST(im, p))
-           {
-#ifdef CLEANDEPSDEBUG
-             printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
-#endif
-             queue_push(iq, p);
-           }
-       }
-      while (dq.elements[i])
-       i++;
-    }
-  queue_free(&dq);
-}
-
-static void
-complex_cleandeps_addback(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq, Map *userinstalled)
-{
-  int i, blk;
-  Queue dq;
-  Id p;
-
-  queue_init(&dq);
-  i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
-  if (i == 0 || i == 1)
-    {
-      queue_free(&dq);
-      return;
-    }
-  for (i = 0; i < dq.count; i++)
-    {
-      blk = i;
-      for (; (p = dq.elements[i]) != 0; i++)
-       {
-         if (p < 0)
-           {
-             if (!MAPTST(installedm, -p))
-               break;
-           }
-         else if (p == ip)
-           break;
-       }
-      if (!p)
-       {
-         for (i = blk; (p = dq.elements[i]) != 0; i++)
-           {
-             if (p < 0)
-               continue;
-             if (MAPTST(im, p))
-               continue;
-             if (!MAPTST(installedm, p))
-               continue;
-             if (p == ip || MAPTST(userinstalled, p - pool->installed->start))
-               continue;
-#ifdef CLEANDEPSDEBUG
-             printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
-#endif
-             MAPSET(im, p);
-             queue_push(iq, p);
-           }
-       }
-      while (dq.elements[i])
-       i++;
-    }
-  queue_free(&dq);
-}
-
-#endif
-
-/*
- * Find all installed packages that are no longer
- * needed regarding the current solver job.
- *
- * The algorithm is:
- * - remove pass: remove all packages that could have
- *   been dragged in by the obsoleted packages.
- *   i.e. if package A is obsolete and contains "Requires: B",
- *   also remove B, as installing A will have pulled in B.
- *   after this pass, we have a set of still installed packages
- *   with broken dependencies.
- * - add back pass:
- *   now add back all packages that the still installed packages
- *   require.
- *
- * The cleandeps packages are the packages removed in the first
- * pass and not added back in the second pass.
- *
- * If we search for unneeded packages (unneeded is true), we
- * simply remove all packages except the userinstalled ones in
- * the first pass.
- */
-static void
-solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
-{
-  Pool *pool = solv->pool;
-  Repo *installed = solv->installed;
-  Queue *job = &solv->job;
-  Map userinstalled;
-  Map im;
-  Map installedm;
-  Rule *r;
-  Id rid, how, what, select;
-  Id p, pp, ip, jp;
-  Id req, *reqp, sup, *supp;
-  Solvable *s;
-  Queue iq, iqcopy, xsuppq;
-  int i;
-
-  map_empty(cleandepsmap);
-  if (!installed || installed->end == installed->start)
-    return;
-  map_init(&userinstalled, installed->end - installed->start);
-  map_init(&im, pool->nsolvables);
-  map_init(&installedm, pool->nsolvables);
-  queue_init(&iq);
-  queue_init(&xsuppq);
-
-  for (i = 0; i < job->count; i += 2)
-    {
-      how = job->elements[i];
-      if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
-       {
-         what = job->elements[i + 1];
-         select = how & SOLVER_SELECTMASK;
-         if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
-           {
-             FOR_REPO_SOLVABLES(installed, p, s)
-               MAPSET(&userinstalled, p - installed->start);
-           }
-         FOR_JOB_SELECT(p, pp, select, what)
-           if (pool->solvables[p].repo == installed)
-             MAPSET(&userinstalled, p - installed->start);
-       }
-      if ((how & (SOLVER_JOBMASK | SOLVER_SELECTMASK)) == (SOLVER_ERASE | SOLVER_SOLVABLE_PROVIDES))
-       {
-         what = job->elements[i + 1];
-         if (ISRELDEP(what))
-           {
-             Reldep *rd = GETRELDEP(pool, what);
-             if (rd->flags != REL_NAMESPACE)
-               continue;
-             if (rd->evr == 0)
-               {
-                 queue_pushunique(&iq, rd->name);
-                 continue;
-               }
-             FOR_PROVIDES(p, pp, what)
-               if (p)
-                 break;
-             if (p)
-               continue;
-             queue_pushunique(&iq, what);
-           }
-       }
-    }
-
-  /* have special namespace cleandeps erases */
-  if (iq.count)
-    {
-      for (ip = installed->start; ip < installed->end; ip++)
-       {
-         s = pool->solvables + ip;
-         if (s->repo != installed)
-           continue;
-         if (!s->supplements)
-           continue;
-         supp = s->repo->idarraydata + s->supplements;
-         while ((sup = *supp++) != 0)
-           if (ISRELDEP(sup) && check_xsupp(solv, &iq, sup) && !check_xsupp(solv, 0, sup))
-             {
-#ifdef CLEANDEPSDEBUG
-               printf("xsupp %s from %s\n", pool_dep2str(pool, sup), pool_solvid2str(pool, ip));
-#endif
-               queue_pushunique(&xsuppq, sup);
-             }
-       }
-      queue_empty(&iq);
-    }
-
-  /* also add visible patterns to userinstalled for openSUSE */
-  if (1)
-    {
-      Dataiterator di;
-      dataiterator_init(&di, pool, 0, 0, SOLVABLE_ISVISIBLE, 0, 0);
-      while (dataiterator_step(&di))
-       {
-         Id *dp;
-         if (di.solvid <= 0)
-           continue;
-         s = pool->solvables + di.solvid;
-         if (!s->repo || !s->requires)
-           continue;
-         if (s->repo != installed && !pool_installable(pool, s))
-           continue;
-         if (strncmp(pool_id2str(pool, s->name), "pattern:", 8) != 0)
-           continue;
-         dp = s->repo->idarraydata + s->requires;
-         for (dp = s->repo->idarraydata + s->requires; *dp; dp++)
-           FOR_PROVIDES(p, pp, *dp)
-             if (pool->solvables[p].repo == installed)
-               {
-                 if (strncmp(pool_id2str(pool, pool->solvables[p].name), "pattern", 7) != 0)
-                   continue;
-                 MAPSET(&userinstalled, p - installed->start);
-               }
-       }
-      dataiterator_free(&di);
-    }
-  if (1)
-    {
-      /* all products and their buddies are userinstalled */
-      for (p = installed->start; p < installed->end; p++)
-       {
-         Solvable *s = pool->solvables + p;
-         if (s->repo != installed)
-           continue;
-         if (!strncmp("product:", pool_id2str(pool, s->name), 8))
-           {
-             MAPSET(&userinstalled, p - installed->start);
-#ifdef ENABLE_LINKED_PKGS
-             if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1)
-               {
-                 Id buddy = solv->instbuddy[p - installed->start];
-                 if (buddy >= installed->start && buddy < installed->end)
-                   MAPSET(&userinstalled, buddy - installed->start);
-               }
-#endif
-           }
-       }
-    }
-
-  /* add all positive elements (e.g. locks) to "userinstalled" */
-  for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
-    {
-      r = solv->rules + rid;
-      if (r->d < 0)
-       continue;
-      i = solv->ruletojob.elements[rid - solv->jobrules];
-      if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS)
-       continue;
-      FOR_RULELITERALS(p, jp, r)
-       if (p > 0 && pool->solvables[p].repo == installed)
-         MAPSET(&userinstalled, p - installed->start);
-    }
-
-  /* add all cleandeps candidates to iq */
-  for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
-    {
-      r = solv->rules + rid;
-      if (r->d < 0)                            /* disabled? */
-       continue;
-      if (r->d == 0 && r->p < 0 && r->w2 == 0) /* negative assertion (erase job)? */
-       {
-         p = -r->p;
-         if (pool->solvables[p].repo != installed)
-           continue;
-         MAPCLR(&userinstalled, p - installed->start);
-         if (unneeded)
-           continue;
-         i = solv->ruletojob.elements[rid - solv->jobrules];
-         how = job->elements[i];
-         if ((how & (SOLVER_JOBMASK|SOLVER_CLEANDEPS)) == (SOLVER_ERASE|SOLVER_CLEANDEPS))
-           queue_push(&iq, p);
-       }
-      else if (r->p > 0)                       /* install job */
-       {
-         if (unneeded)
-           continue;
-         i = solv->ruletojob.elements[rid - solv->jobrules];
-         if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS)
-           {
-             /* check if the literals all obsolete some installed package */
-             Map om;
-             int iqstart;
-
-             /* just one installed literal */
-             if (r->d == 0 && r->w2 == 0 && pool->solvables[r->p].repo == installed)
-               continue;
-             /* multiversion is bad */
-             if (solv->multiversion.size && !solv->keepexplicitobsoletes)
-               {
-                 FOR_RULELITERALS(p, jp, r)
-                   if (MAPTST(&solv->multiversion, p))
-                     break;
-                 if (p)
-                   continue;
-               }
-
-             om.size = 0;
-             iqstart = iq.count;
-             FOR_RULELITERALS(p, jp, r)
-               {
-                 if (p < 0)
-                   {
-                     queue_truncate(&iq, iqstart);     /* abort */
-                     break;
-                   }
-                 if (pool->solvables[p].repo == installed)
-                   {
-                     if (iq.count == iqstart)
-                       queue_push(&iq, p);
-                     else
-                       {
-                         for (i = iqstart; i < iq.count; i++)
-                           if (iq.elements[i] == p)
-                             break;
-                         queue_truncate(&iq, iqstart);
-                         if (i < iq.count)
-                           queue_push(&iq, p);
-                       }
-                   }
-                 else
-                   intersect_obsoletes(solv, p, &iq, iqstart, &om);
-                 if (iq.count == iqstart)
-                   break;
-               }
-             if (om.size)
-               map_free(&om);
-           }
-       }
-    }
-  queue_init_clone(&iqcopy, &iq);
-
-  if (!unneeded)
-    {
-      if (solv->cleandeps_updatepkgs)
-       for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
-         queue_push(&iq, solv->cleandeps_updatepkgs->elements[i]);
-    }
-
-  if (unneeded)
-    queue_empty(&iq);  /* just in case... */
-
-  /* clear userinstalled bit for the packages we really want to delete/update */
-  for (i = 0; i < iq.count; i++)
-    {
-      p = iq.elements[i];
-      if (pool->solvables[p].repo != installed)
-       continue;
-      MAPCLR(&userinstalled, p - installed->start);
-    }
-
-  for (p = installed->start; p < installed->end; p++)
-    {
-      if (pool->solvables[p].repo != installed)
-       continue;
-      MAPSET(&installedm, p);
-      if (unneeded && !MAPTST(&userinstalled, p - installed->start))
-       continue;
-      MAPSET(&im, p);
-    }
-  MAPSET(&installedm, SYSTEMSOLVABLE);
-  MAPSET(&im, SYSTEMSOLVABLE);
-
-#ifdef CLEANDEPSDEBUG
-  printf("REMOVE PASS\n");
-#endif
-
-  for (;;)
-    {
-      if (!iq.count)
-       {
-         if (unneeded)
-           break;
-         /* supplements pass */
-         for (ip = installed->start; ip < installed->end; ip++)
-           {
-             if (!MAPTST(&installedm, ip))
-               continue;
-             s = pool->solvables + ip;
-             if (!s->supplements)
-               continue;
-             if (!MAPTST(&im, ip))
-               continue;
-             if (MAPTST(&userinstalled, ip - installed->start))
-               continue;
-             supp = s->repo->idarraydata + s->supplements;
-             while ((sup = *supp++) != 0)
-               if (dep_possible(solv, sup, &im))
-                 break;
-             if (!sup)
-               {
-                 supp = s->repo->idarraydata + s->supplements;
-                 while ((sup = *supp++) != 0)
-                   if (dep_possible(solv, sup, &installedm) || (xsuppq.count && queue_contains(&xsuppq, sup)))
-                     {
-                       /* no longer supplemented, also erase */
-                       int iqcount = iq.count;
-                       /* pin packages, see comment above dep_pkgcheck */
-                       dep_pkgcheck(solv, sup, &im, &iq);
-                       for (i = iqcount; i < iq.count; i++)
-                         {
-                           Id pqp = iq.elements[i];
-                           if (pool->solvables[pqp].repo == installed)
-                             MAPSET(&userinstalled, pqp - installed->start);
-                         }
-                       queue_truncate(&iq, iqcount);
-#ifdef CLEANDEPSDEBUG
-                       printf("%s supplemented [%s]\n", pool_solvid2str(pool, ip), pool_dep2str(pool, sup));
-#endif
-                       queue_push(&iq, ip);
-                     }
-               }
-           }
-         if (!iq.count)
-           break;      /* no supplementing package found, we're done */
-       }
-      ip = queue_shift(&iq);
-      s = pool->solvables + ip;
-      if (!MAPTST(&im, ip))
-       continue;
-      if (!MAPTST(&installedm, ip))
-       continue;
-      if (s->repo == installed && MAPTST(&userinstalled, ip - installed->start))
-       continue;
-      MAPCLR(&im, ip);
-#ifdef CLEANDEPSDEBUG
-      printf("removing %s\n", pool_solvable2str(pool, s));
-#endif
-      if (s->requires)
-       {
-         reqp = s->repo->idarraydata + s->requires;
-         while ((req = *reqp++) != 0)
-           {
-             if (req == SOLVABLE_PREREQMARKER)
-               continue;
-#ifdef ENABLE_COMPLEX_DEPS
-             if (pool_is_complex_dep(pool, req))
-               {
-                 complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq);
-                 continue;
-               }
-#endif
-             FOR_PROVIDES(p, pp, req)
-               {
-                 if (p != SYSTEMSOLVABLE && MAPTST(&im, p))
-                   {
-#ifdef CLEANDEPSDEBUG
-                     printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
-#endif
-                     queue_push(&iq, p);
-                   }
-               }
-           }
-       }
-      if (s->recommends)
-       {
-         reqp = s->repo->idarraydata + s->recommends;
-         while ((req = *reqp++) != 0)
-           {
-#ifdef ENABLE_COMPLEX_DEPS
-             if (pool_is_complex_dep(pool, req))
-               {
-                 complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq);
-                 continue;
-               }
-#endif
-             FOR_PROVIDES(p, pp, req)
-               {
-                 if (p != SYSTEMSOLVABLE && MAPTST(&im, p))
-                   {
-#ifdef CLEANDEPSDEBUG
-                     printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
-#endif
-                     queue_push(&iq, p);
-                   }
-               }
-           }
-       }
-    }
-
-  /* turn userinstalled into remove set for pruning */
-  map_empty(&userinstalled);
-  for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
-    {
-      r = solv->rules + rid;
-      if (r->p >= 0 || r->d != 0 || r->w2 != 0)
-       continue;       /* disabled or not erase */
-      p = -r->p;
-      MAPCLR(&im, p);
-      if (pool->solvables[p].repo == installed)
-        MAPSET(&userinstalled, p - installed->start);
-    }
-  if (!unneeded && solv->cleandeps_updatepkgs)
-    {
-      for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
-       {
-         p = solv->cleandeps_updatepkgs->elements[i];
-         if (pool->solvables[p].repo == installed)
-           MAPSET(&userinstalled, p - installed->start);
-       }
-    }
-  MAPSET(&im, SYSTEMSOLVABLE); /* in case we cleared it above */
-  for (p = installed->start; p < installed->end; p++)
-    if (MAPTST(&im, p))
-      queue_push(&iq, p);
-  for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
-    {
-      r = solv->rules + rid;
-      if (r->d < 0)
-       continue;
-      FOR_RULELITERALS(p, jp, r)
-       if (p > 0)
-          queue_push(&iq, p);
-    }
-  /* also put directly addressed packages on the install queue
-   * so we can mark patterns as installed */
-  for (i = 0; i < job->count; i += 2)
-    {
-      how = job->elements[i];
-      if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
-       {
-         what = job->elements[i + 1];
-         select = how & SOLVER_SELECTMASK;
-         if (select == SOLVER_SOLVABLE && pool->solvables[what].repo != installed)
-            queue_push(&iq, what);
-       }
-    }
-
-#ifdef CLEANDEPSDEBUG
-  printf("ADDBACK PASS\n");
-#endif
-  for (;;)
-    {
-      if (!iq.count)
-       {
-         /* supplements pass */
-         for (ip = installed->start; ip < installed->end; ip++)
-           {
-             if (!MAPTST(&installedm, ip))
-               continue;
-             if (MAPTST(&userinstalled, ip - installed->start))
-               continue;
-             s = pool->solvables + ip;
-             if (!s->supplements)
-               continue;
-             if (MAPTST(&im, ip))
-               continue;
-             supp = s->repo->idarraydata + s->supplements;
-             while ((sup = *supp++) != 0)
-               if (dep_possible(solv, sup, &im))
-                 break;
-             if (sup)
-               {
-#ifdef CLEANDEPSDEBUG
-                 printf("%s supplemented\n", pool_solvid2str(pool, ip));
-#endif
-                 MAPSET(&im, ip);
-                 queue_push(&iq, ip);
-               }
-           }
-         if (!iq.count)
-           break;
-       }
-      ip = queue_shift(&iq);
-      s = pool->solvables + ip;
-#ifdef CLEANDEPSDEBUG
-      printf("adding back %s\n", pool_solvable2str(pool, s));
-#endif
-      if (s->repo == installed && pool->implicitobsoleteusescolors)
-       {
-         Id a, bestarch = 0;
-         FOR_PROVIDES(p, pp, s->name)
-           {
-             Solvable *ps = pool->solvables + p;
-             if (ps->name != s->name || ps->repo == installed)
-               continue;
-             a = ps->arch;
-             a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
-             if (a && a != 1 && (!bestarch || a < bestarch))
-               bestarch = a;
-           }
-         if (bestarch && (s->arch > pool->lastarch || pool->id2arch[s->arch] != bestarch))
-           {
-             FOR_PROVIDES(p, pp, s->name)
-               {
-                 Solvable *ps = pool->solvables + p;
-                 if (ps->repo == installed && ps->name == s->name && ps->evr == s->evr && ps->arch != s->arch && ps->arch < pool->lastarch && pool->id2arch[ps->arch] == bestarch)
-                   if (!MAPTST(&im, p))
-                     {
-#ifdef CLEANDEPSDEBUG
-                       printf("%s lockstep %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
-#endif
-                       MAPSET(&im, p);
-                       queue_push(&iq, p);
-                     }
-               }
-           }
-       }
-      if (s->requires)
-       {
-         reqp = s->repo->idarraydata + s->requires;
-         while ((req = *reqp++) != 0)
-           {
-#ifdef ENABLE_COMPLEX_DEPS
-             if (pool_is_complex_dep(pool, req))
-               {
-                 complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled);
-                 continue;
-               }
-#endif
-             FOR_PROVIDES(p, pp, req)
-               if (p == ip)
-                 break;
-             if (p)
-               continue;
-             FOR_PROVIDES(p, pp, req)
-               {
-                 if (MAPTST(&im, p))
-                   continue;
-                 if (MAPTST(&installedm, p))
-                   {
-                     if (p == ip)
-                       continue;
-                     if (MAPTST(&userinstalled, p - installed->start))
-                       continue;
-#ifdef CLEANDEPSDEBUG
-                     printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
-#endif
-                     MAPSET(&im, p);
-                     queue_push(&iq, p);
-                   }
-               }
-           }
-       }
-      if (s->recommends)
-       {
-         reqp = s->repo->idarraydata + s->recommends;
-         while ((req = *reqp++) != 0)
-           {
-#ifdef ENABLE_COMPLEX_DEPS
-             if (pool_is_complex_dep(pool, req))
-               {
-                 complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled);
-                 continue;
-               }
-#endif
-             FOR_PROVIDES(p, pp, req)
-               if (p == ip)
-                 break;
-             if (p)
-               continue;
-             FOR_PROVIDES(p, pp, req)
-               {
-                 if (MAPTST(&im, p))
-                   continue;
-                 if (MAPTST(&installedm, p))
-                   {
-                     if (p == ip)
-                       continue;
-                     if (MAPTST(&userinstalled, p - installed->start))
-                       continue;
-#ifdef CLEANDEPSDEBUG
-                     printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
-#endif
-                     MAPSET(&im, p);
-                     queue_push(&iq, p);
-                   }
-               }
-           }
-       }
-    }
-
-  queue_free(&iq);
-  /* make sure the updatepkgs and mistakes are not in the cleandeps map */
-  if (solv->cleandeps_updatepkgs)
-    for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
-      MAPSET(&im, solv->cleandeps_updatepkgs->elements[i]);
-  if (solv->cleandeps_mistakes)
-    for (i = 0; i < solv->cleandeps_mistakes->count; i++)
-      MAPSET(&im, solv->cleandeps_mistakes->elements[i]);
-  /* also remove original iq packages */
-  for (i = 0; i < iqcopy.count; i++)
-    MAPSET(&im, iqcopy.elements[i]);
-  queue_free(&iqcopy);
-  for (p = installed->start; p < installed->end; p++)
-    {
-      if (pool->solvables[p].repo != installed)
-       continue;
-      if (!MAPTST(&im, p))
-        MAPSET(cleandepsmap, p - installed->start);
-    }
-  map_free(&im);
-  map_free(&installedm);
-  map_free(&userinstalled);
-  queue_free(&xsuppq);
-#ifdef CLEANDEPSDEBUG
-  printf("=== final cleandeps map:\n");
-  for (p = installed->start; p < installed->end; p++)
-    if (MAPTST(cleandepsmap, p - installed->start))
-      printf("  - %s\n", pool_solvid2str(pool, p));
-#endif
-}
-
-
-struct trj_data {
-  Queue *edges;
-  Id *low;
-  Id idx;
-  Id nstack;
-  Id firstidx;
-};
-
-/* Tarjan's SCC algorithm, slightly modifed */
-static void
-trj_visit(struct trj_data *trj, Id node)
-{
-  Id *low = trj->low;
-  Queue *edges = trj->edges;
-  Id nnode, myidx, stackstart;
-  int i;
-
-  low[node] = myidx = trj->idx++;
-  low[(stackstart = trj->nstack++)] = node;
-  for (i = edges->elements[node]; (nnode = edges->elements[i]) != 0; i++)
-    {
-      Id l = low[nnode];
-      if (!l)
-       {
-         if (!edges->elements[edges->elements[nnode]])
-           {
-             trj->idx++;
-             low[nnode] = -1;
-             continue;
-           }
-         trj_visit(trj, nnode);
-         l = low[nnode];
-       }
-      if (l < 0)
-       continue;
-      if (l < trj->firstidx)
-       {
-         int k;
-         for (k = l; low[low[k]] == l; k++)
-           low[low[k]] = -1;
-       }
-      else if (l < low[node])
-       low[node] = l;
-    }
-  if (low[node] == myidx)
-    {
-      if (myidx != trj->firstidx)
-       myidx = -1;
-      for (i = stackstart; i < trj->nstack; i++)
-       low[low[i]] = myidx;
-      trj->nstack = stackstart;
-    }
-}
-
-#ifdef ENABLE_COMPLEX_DEPS
-static void
-complex_unneeded(Pool *pool, Id ip, Id req, Queue *edges, Map *cleandepsmap, Queue *unneededq)
-{
-  int i, j;
-  Queue dq;
-  Id p;
-
-  queue_init(&dq);
-  i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
-  if (i == 0 || i == 1)
-    {
-      queue_free(&dq);
-      return;
-    }
-  for (i = 0; i < dq.count; i++)
-    {
-      for (; (p = dq.elements[i]) != 0; i++)
-       {
-         if (p < 0)
-           {
-             if (pool->solvables[-p].repo != pool->installed)
-               break;
-             continue;
-           }
-         if (p == ip || pool->solvables[p].repo != pool->installed || !MAPTST(cleandepsmap, p - pool->installed->start))
-           continue;
-         for (j = 0; j < unneededq->count; j++)
-           if (p == unneededq->elements[j])
-             {
-               if (edges->elements[edges->count - 1] != j + 1)
-                 queue_push(edges, j + 1);
-               break;
-             }
-       }
-      while (dq.elements[i])
-       i++;
-    }
-  queue_free(&dq);
-}
-#endif
-
-void
-solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered)
-{
-  Repo *installed = solv->installed;
-  int i;
-  Map cleandepsmap;
-
-  queue_empty(unneededq);
-  if (!installed || installed->end == installed->start)
-    return;
-
-  map_init(&cleandepsmap, installed->end - installed->start);
-  solver_createcleandepsmap(solv, &cleandepsmap, 1);
-  for (i = installed->start; i < installed->end; i++)
-    if (MAPTST(&cleandepsmap, i - installed->start))
-      queue_push(unneededq, i);
-
-  if (filtered && unneededq->count > 1)
-    {
-      Pool *pool = solv->pool;
-      Queue edges;
-      Id *nrequires;
-      Map installedm;
-      int j, pass, count = unneededq->count;
-      Id *low;
-
-      map_init(&installedm, pool->nsolvables);
-      for (i = installed->start; i < installed->end; i++)
-       if (pool->solvables[i].repo == installed)
-         MAPSET(&installedm, i);
-
-      nrequires = solv_calloc(count, sizeof(Id));
-      queue_init(&edges);
-      queue_prealloc(&edges, count * 4 + 10);  /* pre-size */
-
-      /*
-       * Go through the solvables in the nodes queue and create edges for
-       * all requires/recommends/supplements between the nodes.
-       * The edges are stored in the edges queue, we add 1 to the node
-       * index so that nodes in the edges queue are != 0 and we can
-       * terminate the edge list with 0.
-       * Thus for node element 5, the edges are stored starting at
-       * edges.elements[6] and are 0-terminated.
-       */
-      /* leave first element zero to make things easier */
-      /* also add trailing zero */
-      queue_insertn(&edges, 0, 1 + count + 1, 0);
-
-      /* first requires and recommends */
-      for (i = 0; i < count; i++)
-       {
-         Solvable *s = pool->solvables + unneededq->elements[i];
-         int oldcount = edges.count;
-         edges.elements[i + 1] = oldcount;
-         for (pass = 0; pass < 2; pass++)
-           {
-             unsigned int off = pass == 0 ? s->requires : s->recommends;
-             Id p, pp, dep, *dp;
-             if (off)
-               for (dp = s->repo->idarraydata + off; (dep = *dp) != 0; dp++)
-                 {
-#ifdef ENABLE_COMPLEX_DEPS
-                   if (pool_is_complex_dep(pool, dep))
-                     {
-                       complex_unneeded(pool, s - pool->solvables, dep, &edges, &cleandepsmap, unneededq);
-                       continue;
-                     }
-#endif
-                   FOR_PROVIDES(p, pp, dep)
-                     {
-                       Solvable *sp = pool->solvables + p;
-                       if (s == sp || sp->repo != installed || !MAPTST(&cleandepsmap, p - installed->start))
-                         continue;
-                       for (j = 0; j < count; j++)
-                         if (p == unneededq->elements[j])
-                           {
-                             if (edges.elements[edges.count - 1] != j + 1)
-                               queue_push(&edges, j + 1);
-                           }
-                     }
-                 }
-             if (pass == 0)
-               nrequires[i] = edges.count - oldcount;
-           }
-         queue_push(&edges, 0);
-       }
-#if 0
-      printf("requires + recommends\n");
-      for (i = 0; i < count; i++)
-       {
-         int j;
-         printf("  %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]);
-         for (j = edges.elements[i + 1]; edges.elements[j]; j++)
-           printf("    - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1]));
-       }
-#endif
-
-      /* then add supplements */
-      for (i = 0; i < count; i++)
-       {
-         Solvable *s = pool->solvables + unneededq->elements[i];
-         if (s->supplements)
-           {
-             Id *dp;
-             int k;
-             for (dp = s->repo->idarraydata + s->supplements; *dp; dp++)
-               if (dep_possible(solv, *dp, &installedm))
-                 {
-                   Queue iq;
-                   Id iqbuf[16];
-                   queue_init_buffer(&iq, iqbuf, sizeof(iqbuf)/sizeof(*iqbuf));
-                   dep_pkgcheck(solv, *dp, 0, &iq);
-                   for (k = 0; k < iq.count; k++)
-                     {
-                       Id p = iq.elements[k];
-                       Solvable *sp = pool->solvables + p;
-                       if (p == unneededq->elements[i] || sp->repo != installed || !MAPTST(&cleandepsmap, p - installed->start))
-                         continue;
-                       for (j = 0; j < count; j++)
-                         if (p == unneededq->elements[j])
-                           break;
-                       /* now add edge from j + 1 to i + 1 */
-                       queue_insert(&edges, edges.elements[j + 1] + nrequires[j], i + 1);
-                       /* addapt following edge pointers */
-                       for (j = j + 2; j < count + 1; j++)
-                         edges.elements[j]++;
-                     }
-                   queue_free(&iq);
-                 }
-           }
-       }
-#if 0
-      /* print result */
-      printf("+ supplements\n");
-      for (i = 0; i < count; i++)
-       {
-         int j;
-         printf("  %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]);
-         for (j = edges.elements[i + 1]; edges.elements[j]; j++)
-           printf("    - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1]));
-        }
-#endif
-      map_free(&installedm);
-
-      /* now run SCC algo two times, first with requires+recommends+supplements,
-       * then again without the requires. We run it the second time to get rid
-       * of packages that got dragged in via recommends/supplements */
-      /*
-       * low will contain the result of the SCC search.
-       * it must be of at least size 2 * (count + 1) and
-       * must be zero initialized.
-       * The layout is:
-       *    0  low low ... low stack stack ...stack 0
-       *            count              count
-       */
-      low = solv_calloc(count + 1, 2 * sizeof(Id));
-      for (pass = 0; pass < 2; pass++)
-       {
-         struct trj_data trj;
-         if (pass)
-           {
-             memset(low, 0, (count + 1) * (2 * sizeof(Id)));
-             for (i = 0; i < count; i++)
-               {
-                 edges.elements[i + 1] += nrequires[i];
-                 if (!unneededq->elements[i])
-                   low[i + 1] = -1;    /* ignore this node */
-               }
-           }
-         trj.edges = &edges;
-         trj.low = low;
-         trj.idx = count + 1;  /* stack starts here */
-         for (i = 1; i <= count; i++)
-           {
-             if (low[i])
-               continue;
-             if (edges.elements[edges.elements[i]])
-               {
-                 trj.firstidx = trj.nstack = trj.idx;
-                 trj_visit(&trj, i);
-               }
-             else
-               {
-                 Id myidx = trj.idx++;
-                 low[i] = myidx;
-                 low[myidx] = i;
-               }
-           }
-         /* prune packages */
-         for (i = 0; i < count; i++)
-           if (low[i + 1] <= 0)
-             unneededq->elements[i] = 0;
-       }
-      solv_free(low);
-      solv_free(nrequires);
-      queue_free(&edges);
-
-      /* finally remove all pruned entries from unneededq */
-      for (i = j = 0; i < count; i++)
-       if (unneededq->elements[i])
-         unneededq->elements[j++] = unneededq->elements[i];
-      queue_truncate(unneededq, j);
-    }
-  map_free(&cleandepsmap);
-}
-
-
 void
 solver_breakorphans(Solver *solv)
 {
 void
 solver_breakorphans(Solver *solv)
 {
index 5d1052d..037b33d 100644 (file)
 #define RULES_BLOCK 63
 
 
 #define RULES_BLOCK 63
 
 
-/********************************************************************
- *
- * dependency check helpers
- *
- */
-
-/*-------------------------------------------------------------------
- * handle split provides
- *
- * a splitprovides dep looks like
- *     namespace:splitprovides(pkg REL_WITH path)
- * and is only true if pkg is installed and contains the specified path.
- * we also make sure that pkg is selected for an update, otherwise the
- * update would always be forced onto the user.
- * Map m is the map used when called from dep_possible.
- */
-
-static int
-solver_is_updating(Solver *solv, Id p)
-{
-  /* check if the update rule is true */
-  Pool *pool = solv->pool;
-  Rule *r;
-  Id l, pp;
-  if (solv->decisionmap[p] >= 0)
-    return 0;  /* old package stayed */
-  r = solv->rules + solv->updaterules + (p - solv->installed->start);
-  FOR_RULELITERALS(l, pp, r)
-    if (l > 0 && l != p && solv->decisionmap[l] > 0)
-      return 1;
-  return 0;
-}
-
-int
-solver_splitprovides(Solver *solv, Id dep, Map *m)
-{
-  Pool *pool = solv->pool;
-  Id p, pp;
-  Reldep *rd;
-  Solvable *s;
-
-  if (!solv->dosplitprovides || !solv->installed)
-    return 0;
-  if (!ISRELDEP(dep))
-    return 0;
-  rd = GETRELDEP(pool, dep);
-  if (rd->flags != REL_WITH)
-    return 0;
-  /*
-   * things are a bit tricky here if pool->addedprovides == 1, because most split-provides are in
-   * a non-standard location. If we simply call pool_whatprovides, we'll drag in the complete
-   * file list. Instead we rely on pool_addfileprovides ignoring the addfileprovidesfiltered flag
-   * for installed packages and check the lazywhatprovidesq (ignoring the REL_WITH part, but
-   * we filter the package name further down anyway).
-   */
-  if (pool->addedfileprovides == 1 && !ISRELDEP(rd->evr) && !pool->whatprovides[rd->evr])
-    pp = pool_searchlazywhatprovidesq(pool, rd->evr);
-  else
-    pp = pool_whatprovides(pool, dep);
-  while ((p = pool->whatprovidesdata[pp++]) != 0)
-    {
-      /* here we have packages that provide the correct name and contain the path,
-       * now do extra filtering */
-      s = pool->solvables + p;
-      if (s->repo != solv->installed || s->name != rd->name)
-       continue;
-      /* check if the package is updated. if m is set, we're called from dep_possible */
-      if (m || solver_is_updating(solv, p))
-       return 1;
-    }
-  return 0;
-}
-
-int
-solver_dep_fulfilled_cplx(Solver *solv, Reldep *rd)
-{
-  Pool *pool = solv->pool;
-  if (rd->flags == REL_COND)
-    {
-      if (ISRELDEP(rd->evr))
-       {
-         Reldep *rd2 = GETRELDEP(pool, rd->evr);
-         if (rd2->flags == REL_ELSE)
-           {
-             if (solver_dep_fulfilled(solv, rd2->name))
-               return solver_dep_fulfilled(solv, rd->name);
-             return solver_dep_fulfilled(solv, rd2->evr);
-           }
-       }
-      if (solver_dep_fulfilled(solv, rd->name))
-       return 1;
-      return !solver_dep_fulfilled(solv, rd->evr);
-    }
-  if (rd->flags == REL_UNLESS)
-    {
-      if (ISRELDEP(rd->evr))
-       {
-         Reldep *rd2 = GETRELDEP(pool, rd->evr);
-         if (rd2->flags == REL_ELSE)
-           {
-             if (!solver_dep_fulfilled(solv, rd2->name))
-               return solver_dep_fulfilled(solv, rd->name);
-             return solver_dep_fulfilled(solv, rd2->evr);
-           }
-       }
-      if (!solver_dep_fulfilled(solv, rd->name))
-       return 0;
-      return !solver_dep_fulfilled(solv, rd->evr);
-    }
-  if (rd->flags == REL_AND)
-    {
-      if (!solver_dep_fulfilled(solv, rd->name))
-       return 0;
-      return solver_dep_fulfilled(solv, rd->evr);
-    }
-  if (rd->flags == REL_OR)
-    {
-      if (solver_dep_fulfilled(solv, rd->name))
-       return 1;
-      return solver_dep_fulfilled(solv, rd->evr);
-    }
-  return 0;
-}
-
-
-/* mirrors solver_dep_fulfilled, but returns 2 if a new package
- * was involved */
-static int
-solver_dep_fulfilled_alreadyinstalled(Solver *solv, Id dep)
-{
-  Pool *pool = solv->pool;
-  Id p, pp;
-  int r;
-
-  if (ISRELDEP(dep))
-    {
-      Reldep *rd = GETRELDEP(pool, dep);
-      if (rd->flags == REL_COND)
-       {
-         int r1, r2;
-         if (ISRELDEP(rd->evr))
-           {
-             Reldep *rd2 = GETRELDEP(pool, rd->evr);
-             if (rd2->flags == REL_ELSE)
-               {
-                 r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->name);
-                 if (r1)
-                   {
-                     r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
-                     return r2 && r1 == 2 ? 2 : r2;
-                   }
-                 return solver_dep_fulfilled_alreadyinstalled(solv, rd2->evr);
-               }
-           }
-         r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
-         r2 = !solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
-         if (!r1 && !r2)
-           return 0;
-          return r1 == 2 ? 2 : 1;
-       }
-      if (rd->flags == REL_UNLESS)
-       {
-         int r1, r2;
-         if (ISRELDEP(rd->evr))
-           {
-             Reldep *rd2 = GETRELDEP(pool, rd->evr);
-             if (rd2->flags == REL_ELSE)
-               {
-                 r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->name);
-                 if (r1)
-                   {
-                     r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->evr);
-                     return r2 && r1 == 2 ? 2 : r2;
-                   }
-                 return solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
-               }
-           }
-         /* A AND NOT(B) */
-         r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
-         r2 = !solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
-         if (!r1 || !r2)
-           return 0;
-          return r1 == 2 ? 2 : 1;
-       }
-      if (rd->flags == REL_AND)
-        {
-         int r2, r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
-          if (!r1)
-            return 0;
-         r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
-         if (!r2)
-           return 0;
-          return r1 == 2 || r2 == 2 ? 2 : 1;
-        }
-      if (rd->flags == REL_OR)
-       {
-         int r2, r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
-         r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
-         if (!r1 && !r2)
-           return 0;
-          return r1 == 2 || r2 == 2 ? 2 : 1;
-       }
-      if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
-        return solver_splitprovides(solv, rd->evr, 0);
-      if (rd->flags == REL_NAMESPACE && solv->installsuppdepq)
-       {
-         Queue *q = solv->installsuppdepq;
-         int i;
-         for (i = 0; i < q->count; i++)
-           if (q->elements[i] == dep || q->elements[i] == rd->name)
-             return 2;
-       }
-    }
-  r = 0;
-  FOR_PROVIDES(p, pp, dep)
-    if (solv->decisionmap[p] > 0)
-      {
-       Solvable *s = pool->solvables + p;
-       if (s->repo && s->repo != solv->installed)
-         return 2;
-        r = 1;
-      }
-  return r;
-}
-
-static int
-solver_is_supplementing_alreadyinstalled(Solver *solv, Solvable *s)
-{
-  Id sup, *supp;
-  supp = s->repo->idarraydata + s->supplements;
-  while ((sup = *supp++) != 0)
-    if (solver_dep_fulfilled_alreadyinstalled(solv, sup) == 2)
-      return 1;
-  return 0;
-}
-
 static Id
 autouninstall(Solver *solv, Id *problem)
 {
 static Id
 autouninstall(Solver *solv, Id *problem)
 {
@@ -495,6 +259,10 @@ makeruledecisions(Solver *solv)
              continue;
            }
 
              continue;
            }
 
+         POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "ANALYZE UNSOLVABLE ASSERTION ----------------------\n");
+         IF_POOLDEBUG (SOLV_DEBUG_UNSOLVABLE)
+           solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + ri);
+
          /*
           * find the decision which is the "opposite" of the rule
           */
          /*
           * find the decision which is the "opposite" of the rule
           */
@@ -540,6 +308,8 @@ makeruledecisions(Solver *solv)
            }
 
          assert(solv->decisionq_why.elements[i] > 0);
            }
 
          assert(solv->decisionq_why.elements[i] > 0);
+         IF_POOLDEBUG (SOLV_DEBUG_UNSOLVABLE)
+           solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + solv->decisionq_why.elements[i]);
 
          /*
           * conflict with a pkg rule ?
 
          /*
           * conflict with a pkg rule ?
@@ -2002,56 +1772,6 @@ resolve_jobrules(Solver *solv, int level, int disablerules, Queue *dq)
   return level;
 }
 
   return level;
 }
 
-static int
-cleandeps_check_mistakes(Solver *solv)
-{
-  Pool *pool = solv->pool;
-  Rule *r;
-  Id p, pp;
-  int i;
-  int mademistake = 0;
-
-  if (!solv->cleandepsmap.size)
-    return 0;
-  /* check for mistakes */
-  for (i = solv->installed->start; i < solv->installed->end; i++)
-    {
-      if (!MAPTST(&solv->cleandepsmap, i - solv->installed->start))
-       continue;
-      r = solv->rules + solv->featurerules + (i - solv->installed->start);
-      /* a mistake is when the featurerule is true but the updaterule is false */
-      if (!r->p)
-       continue;
-      FOR_RULELITERALS(p, pp, r)
-       if (p > 0 && solv->decisionmap[p] > 0)
-         break;
-      if (!p)
-       continue;       /* feature rule is not true */
-      r = solv->rules + solv->updaterules + (i - solv->installed->start);
-      if (!r->p)
-       continue;
-      FOR_RULELITERALS(p, pp, r)
-       if (p > 0 && solv->decisionmap[p] > 0)
-         break;
-      if (p)
-       continue;       /* update rule is true */
-      POOL_DEBUG(SOLV_DEBUG_SOLVER, "cleandeps mistake: ");
-      solver_printruleclass(solv, SOLV_DEBUG_SOLVER, r);
-      POOL_DEBUG(SOLV_DEBUG_SOLVER, "feature rule: ");
-      solver_printruleclass(solv, SOLV_DEBUG_SOLVER, solv->rules + solv->featurerules + (i - solv->installed->start));
-      if (!solv->cleandeps_mistakes)
-       {
-         solv->cleandeps_mistakes = solv_calloc(1, sizeof(Queue));
-         queue_init(solv->cleandeps_mistakes);
-       }
-      queue_push(solv->cleandeps_mistakes, i);
-      MAPCLR(&solv->cleandepsmap, i - solv->installed->start);
-      solver_reenablepolicyrules_cleandeps(solv, i);
-      mademistake = 1;
-    }
-  return mademistake;
-}
-
 static void
 prune_to_update_targets(Solver *solv, Id *cp, Queue *q)
 {
 static void
 prune_to_update_targets(Solver *solv, Id *cp, Queue *q)
 {
@@ -2398,7 +2118,7 @@ add_complex_recommends(Solver *solv, Id rec, Queue *dq, Map *dqmap)
            {
              if (solv->decisionmap[p] < 0)
                continue;
            {
              if (solv->decisionmap[p] < 0)
                continue;
-             if (solv->dupmap_all && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start))))
+             if (solv->process_orphans && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start))))
                continue;
            }
          queue_push(dq, p);
                continue;
            }
          queue_push(dq, p);
@@ -2553,7 +2273,7 @@ resolve_weak(Solver *solv, int level, int disablerules, Queue *dq, Queue *dqs, i
                        }
                      else if (solv->decisionmap[p] == 0)
                        {
                        }
                      else if (solv->decisionmap[p] == 0)
                        {
-                         if (solv->dupmap_all && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start))))
+                         if (solv->process_orphans && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start))))
                            continue;
                          queue_pushunique(dq, p);
                        }
                            continue;
                          queue_pushunique(dq, p);
                        }
@@ -2570,7 +2290,7 @@ resolve_weak(Solver *solv, int level, int disablerules, Queue *dq, Queue *dqs, i
            continue;
          if (!solver_is_supplementing(solv, s))
            continue;
            continue;
          if (!solver_is_supplementing(solv, s))
            continue;
-         if (solv->dupmap_all && solv->installed && s->repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, i - solv->installed->start))))
+         if (solv->process_orphans && solv->installed && s->repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, i - solv->installed->start))))
            continue;
          if (solv->isdisfavormap.size && MAPTST(&solv->isdisfavormap, i))
            continue;   /* disfavored supplements, do not install */
            continue;
          if (solv->isdisfavormap.size && MAPTST(&solv->isdisfavormap, i))
            continue;   /* disfavored supplements, do not install */
@@ -2794,6 +2514,35 @@ resolve_weak(Solver *solv, int level, int disablerules, Queue *dq, Queue *dqs, i
   return level;
 }
 
   return level;
 }
 
+static int
+resolve_cleandeps(Solver *solv, int level, int disablerules, int *rerunp)
+{
+  Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
+  int olevel;
+  Id p;
+  Solvable *s;
+
+  if (!installed || !solv->cleandepsmap.size)
+    return level;
+  POOL_DEBUG(SOLV_DEBUG_SOLVER, "deciding cleandeps packages\n");
+  for (p = installed->start; p < installed->end; p++)
+    {
+      s = pool->solvables + p;
+      if (s->repo != installed)
+       continue;
+      if (solv->decisionmap[p] != 0 || !MAPTST(&solv->cleandepsmap, p - installed->start))
+       continue;
+      POOL_DEBUG(SOLV_DEBUG_POLICY, "cleandeps erasing %s\n", pool_solvid2str(pool, p));
+      olevel = level;
+      level = setpropagatelearn(solv, level, -p, 0, 0, SOLVER_REASON_CLEANDEPS_ERASE);
+      if (level < olevel)
+       break;
+    }
+  if (p < installed->end)
+    *rerunp = 1;
+  return level;
+}
 
 static int
 resolve_orphaned(Solver *solv, int level, int disablerules, Queue *dq, int *rerunp)
 
 static int
 resolve_orphaned(Solver *solv, int level, int disablerules, Queue *dq, int *rerunp)
@@ -2999,24 +2748,12 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
 
       /* decide leftover cleandeps packages */
       if (solv->cleandepsmap.size && solv->installed)
 
       /* decide leftover cleandeps packages */
       if (solv->cleandepsmap.size && solv->installed)
-       {
-         for (p = solv->installed->start; p < solv->installed->end; p++)
-           {
-             s = pool->solvables + p;
-             if (s->repo != solv->installed)
-               continue;
-             if (solv->decisionmap[p] == 0 && MAPTST(&solv->cleandepsmap, p - solv->installed->start))
-               {
-                 POOL_DEBUG(SOLV_DEBUG_POLICY, "cleandeps erasing %s\n", pool_solvid2str(pool, p));
-                 olevel = level;
-                 level = setpropagatelearn(solv, level, -p, 0, 0, SOLVER_REASON_CLEANDEPS_ERASE);
-                 if (level < olevel)
-                   break;
-               }
-           }
-         if (p < solv->installed->end)
-           continue;
-       }
+       {
+         int rerun = 0;
+         level = resolve_cleandeps(solv, level, disablerules, &rerun);
+         if (rerun)
+           continue;
+       }
 
       /* at this point we have a consistent system. now do the extras... */
 
 
       /* at this point we have a consistent system. now do the extras... */
 
@@ -3056,7 +2793,7 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
            continue;           /* back to main loop */
        }
 
            continue;           /* back to main loop */
        }
 
-      if (solv->installed && solv->cleandepsmap.size && cleandeps_check_mistakes(solv))
+      if (solv->installed && solv->cleandepsmap.size && solver_check_cleandeps_mistakes(solv))
        {
          solver_reset(solv);
          level = 0;    /* restart from scratch */
        {
          solver_reset(solv);
          level = 0;    /* restart from scratch */
@@ -3366,7 +3103,7 @@ solver_addjobrule(Solver *solv, Id p, Id p2, Id d, Id job, int weak)
 }
 
 static inline void
 }
 
 static inline void
-add_cleandeps_package(Solver *solv, Id p)
+add_cleandeps_updatepkg(Solver *solv, Id p)
 {
   if (!solv->cleandeps_updatepkgs)
     {
 {
   if (!solv->cleandeps_updatepkgs)
     {
@@ -3405,7 +3142,7 @@ add_update_target(Solver *solv, Id p, Id how)
          MAPSET(&solv->bestupdatemap, pi - installed->start);
        }
       if (how & SOLVER_CLEANDEPS)
          MAPSET(&solv->bestupdatemap, pi - installed->start);
        }
       if (how & SOLVER_CLEANDEPS)
-       add_cleandeps_package(solv, pi);
+       add_cleandeps_updatepkg(solv, pi);
       queue_push2(solv->update_targets, pi, p);
       /* check if it's ok to keep the installed package */
       if (s->evr == si->evr && solvable_identical(s, si))
       queue_push2(solv->update_targets, pi, p);
       /* check if it's ok to keep the installed package */
       if (s->evr == si->evr && solvable_identical(s, si))
@@ -3434,7 +3171,7 @@ add_update_target(Solver *solv, Id p, Id how)
                  MAPSET(&solv->bestupdatemap, pi - installed->start);
                }
              if (how & SOLVER_CLEANDEPS)
                  MAPSET(&solv->bestupdatemap, pi - installed->start);
                }
              if (how & SOLVER_CLEANDEPS)
-               add_cleandeps_package(solv, pi);
+               add_cleandeps_updatepkg(solv, pi);
              queue_push2(solv->update_targets, pi, p);
            }
        }
              queue_push2(solv->update_targets, pi, p);
            }
        }
@@ -3677,7 +3414,7 @@ solver_solve(Solver *solv, Queue *job)
     queue_insertn(&solv->job, 0, pool->pooljobs.count, pool->pooljobs.elements);
   job = &solv->job;
 
     queue_insertn(&solv->job, 0, pool->pooljobs.count, pool->pooljobs.elements);
   job = &solv->job;
 
-  /* free old stuff in jase we re-run a solver */
+  /* free old stuff in case we re-run a solver */
   queuep_free(&solv->update_targets);
   queuep_free(&solv->cleandeps_updatepkgs);
   queue_empty(&solv->ruleassertions);
   queuep_free(&solv->update_targets);
   queuep_free(&solv->cleandeps_updatepkgs);
   queue_empty(&solv->ruleassertions);
@@ -3693,9 +3430,9 @@ solver_solve(Solver *solv, Queue *job)
   map_zerosize(&solv->bestupdatemap);
   solv->fixmap_all = 0;
   map_zerosize(&solv->fixmap);
   map_zerosize(&solv->bestupdatemap);
   solv->fixmap_all = 0;
   map_zerosize(&solv->fixmap);
-  solv->dupmap_all = 0;
   map_zerosize(&solv->dupmap);
   map_zerosize(&solv->dupinvolvedmap);
   map_zerosize(&solv->dupmap);
   map_zerosize(&solv->dupinvolvedmap);
+  solv->process_orphans = 0;
   solv->droporphanedmap_all = 0;
   map_zerosize(&solv->droporphanedmap);
   solv->allowuninstall_all = 0;
   solv->droporphanedmap_all = 0;
   map_zerosize(&solv->droporphanedmap);
   solv->allowuninstall_all = 0;
@@ -3794,7 +3531,7 @@ solver_solve(Solver *solv, Queue *job)
                  if (how & SOLVER_CLEANDEPS)
                    {
                      FOR_REPO_SOLVABLES(installed, p, s)
                  if (how & SOLVER_CLEANDEPS)
                    {
                      FOR_REPO_SOLVABLES(installed, p, s)
-                       add_cleandeps_package(solv, p);
+                       add_cleandeps_updatepkg(solv, p);
                    }
                }
              else if (select == SOLVER_SOLVABLE_REPO)
                    }
                }
              else if (select == SOLVER_SOLVABLE_REPO)
@@ -3810,7 +3547,7 @@ solver_solve(Solver *solv, Queue *job)
                      if (how & SOLVER_CLEANDEPS)
                        {
                          FOR_REPO_SOLVABLES(installed, p, s)
                      if (how & SOLVER_CLEANDEPS)
                        {
                          FOR_REPO_SOLVABLES(installed, p, s)
-                           add_cleandeps_package(solv, p);
+                           add_cleandeps_updatepkg(solv, p);
                        }
                      break;
                    }
                        }
                      break;
                    }
@@ -3840,7 +3577,7 @@ solver_solve(Solver *solv, Queue *job)
                              MAPSET(&solv->bestupdatemap, p - installed->start);
                            }
                          if (how & SOLVER_CLEANDEPS)
                              MAPSET(&solv->bestupdatemap, p - installed->start);
                            }
                          if (how & SOLVER_CLEANDEPS)
-                           add_cleandeps_package(solv, p);
+                           add_cleandeps_updatepkg(solv, p);
                          targeted = 0;
                        }
                      if (!targeted || solv->noautotarget)
                          targeted = 0;
                        }
                      if (!targeted || solv->noautotarget)
@@ -3916,17 +3653,9 @@ solver_solve(Solver *solv, Queue *job)
            }
          break;
        case SOLVER_DISTUPGRADE:
            }
          break;
        case SOLVER_DISTUPGRADE:
+         needduprules = 1;
          if (select == SOLVER_SOLVABLE_ALL)
          if (select == SOLVER_SOLVABLE_ALL)
-           {
-             solv->dupmap_all = 1;
-             solv->updatemap_all = 1;
-             if (how & SOLVER_FORCEBEST)
-               solv->bestupdatemap_all = 1;
-           }
-         if ((how & SOLVER_TARGETED) != 0)
-           needduprules = 1;
-         if (!solv->dupmap_all || solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size || solv->keep_orphans)
-           needduprules = 1;
+           solv->process_orphans = 1;
          break;
        default:
          break;
          break;
        default:
          break;
@@ -4049,7 +3778,7 @@ solver_solve(Solver *solv, Queue *job)
              continue;
            }
          /* it's also orphaned if the feature rule consists just of the installed package */
              continue;
            }
          /* it's also orphaned if the feature rule consists just of the installed package */
-         if (!solv->dupmap_all && sr->p == i && !sr->d && !sr->w2)
+         if (!solv->process_orphans && sr->p == i && !sr->d && !sr->w2)
            queue_push(&solv->orphaned, i);
 
          if (!solver_rulecmp(solv, r, sr))
            queue_push(&solv->orphaned, i);
 
          if (!solver_rulecmp(solv, r, sr))
@@ -4307,7 +4036,7 @@ solver_solve(Solver *solv, Queue *job)
   else
     solv->infarchrules = solv->infarchrules_end = solv->nrules;
 
   else
     solv->infarchrules = solv->infarchrules_end = solv->nrules;
 
-  if (needduprules)
+  if (solv->dupinvolvedmap_all || solv->dupinvolvedmap.size)
     solver_addduprules(solv, &addedmap);
   else
     solv->duprules = solv->duprules_end = solv->nrules;
     solver_addduprules(solv, &addedmap);
   else
     solv->duprules = solv->duprules_end = solv->nrules;
@@ -4398,7 +4127,7 @@ solver_solve(Solver *solv, Queue *job)
   solver_disablepolicyrules(solv);
 
   /* break orphans if requested */
   solver_disablepolicyrules(solv);
 
   /* break orphans if requested */
-  if (solv->dupmap_all && solv->orphaned.count && solv->break_orphans)
+  if (solv->process_orphans && solv->orphaned.count && solv->break_orphans)
     solver_breakorphans(solv);
 
   /*
     solver_breakorphans(solv);
 
   /*
@@ -4435,6 +4164,35 @@ void solver_get_orphaned(Solver *solv, Queue *orphanedq)
   queue_init_clone(orphanedq, &solv->orphaned);
 }
 
   queue_init_clone(orphanedq, &solv->orphaned);
 }
 
+void solver_get_cleandeps(Solver *solv, Queue *cleandepsq)
+{
+  Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
+  Solvable *s;
+  Rule *r;
+  Id p, pp, pr;
+
+  queue_empty(cleandepsq);
+  if (!installed || !solv->cleandepsmap.size)
+    return;
+  FOR_REPO_SOLVABLES(installed, p, s)
+    {
+      if (!MAPTST(&solv->cleandepsmap, p - installed->start) || solv->decisionmap[p] >= 0)
+       continue;
+      /* now check the update rule */
+      r = solv->rules + solv->updaterules + (p - solv->installed->start);
+      if (r->p)
+       {
+         FOR_RULELITERALS(pr, pp, r)
+           if (solv->decisionmap[pr] > 0)
+             break;
+         if (pr)
+           continue;
+       }
+      queue_push(cleandepsq, p);
+    }
+}
+
 void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *suggestionsq, int noselected)
 {
   Pool *pool = solv->pool;
 void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *suggestionsq, int noselected)
 {
   Pool *pool = solv->pool;
@@ -4805,7 +4563,7 @@ solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq)
   for (i = 1; i < pool->nsolvables; i++)
     {
       Id *recp, rec, pp2, p2;
   for (i = 1; i < pool->nsolvables; i++)
     {
       Id *recp, rec, pp2, p2;
-      if (solv->decisionmap[i] < 0 || solv->decisionmap[i] >= level)
+      if (solv->decisionmap[i] <= 0 || solv->decisionmap[i] >= level)
        continue;
       s = pool->solvables + i;
       if (!s->recommends)
        continue;
       s = pool->solvables + i;
       if (!s->recommends)
index 15fdf3f..1b85fb3 100644 (file)
@@ -167,9 +167,10 @@ struct _Solver {
   int strongrecommends;                        /* true: create weak rules for recommends */
   int install_also_updates;            /* true: do not prune install job rules to installed packages */
 
   int strongrecommends;                        /* true: create weak rules for recommends */
   int install_also_updates;            /* true: do not prune install job rules to installed packages */
 
-  Map dupmap;                          /* dup these packages*/
-  int dupmap_all;                      /* dup all packages */
+  int process_orphans;                 /* true: do special orphan processing */
+  Map dupmap;                          /* dup to those packages */
   Map dupinvolvedmap;                  /* packages involved in dup process */
   Map dupinvolvedmap;                  /* packages involved in dup process */
+  int dupinvolvedmap_all;              /* all packages are involved */
   int dup_allowdowngrade;              /* dup mode: allow to downgrade installed solvable */
   int dup_allownamechange;             /* dup mode: allow to change name of installed solvable */
   int dup_allowarchchange;             /* dup mode: allow to change architecture of installed solvables */
   int dup_allowdowngrade;              /* dup mode: allow to downgrade installed solvable */
   int dup_allownamechange;             /* dup mode: allow to change name of installed solvable */
   int dup_allowarchchange;             /* dup mode: allow to change architecture of installed solvables */
@@ -341,6 +342,7 @@ extern void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Qu
 extern void solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered);
 extern void solver_get_userinstalled(Solver *solv, Queue *q, int flags);
 extern void pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags);
 extern void solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered);
 extern void solver_get_userinstalled(Solver *solv, Queue *q, int flags);
 extern void pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags);
+extern void solver_get_cleandeps(Solver *solv, Queue *cleandepsq);
 
 extern int  solver_describe_decision(Solver *solv, Id p, Id *infop);
 extern void solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq);
 
 extern int  solver_describe_decision(Solver *solv, Id p, Id *infop);
 extern void solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq);
index a234223..e92617d 100644 (file)
@@ -17,7 +17,32 @@ extern void solver_run_sat(Solver *solv, int disablerules, int doweak);
 extern void solver_reset(Solver *solv);
 
 extern int solver_splitprovides(Solver *solv, Id dep, Map *m);
 extern void solver_reset(Solver *solv);
 
 extern int solver_splitprovides(Solver *solv, Id dep, Map *m);
+extern int solver_dep_possible_slow(Solver *solv, Id dep, Map *m);
 extern int solver_dep_fulfilled_cplx(Solver *solv, Reldep *rd);
 extern int solver_dep_fulfilled_cplx(Solver *solv, Reldep *rd);
+extern int solver_is_supplementing_alreadyinstalled(Solver *solv, Solvable *s);
+extern void solver_intersect_obsoleted(Solver *solv, Id p, Queue *q, int qstart, Map *m);
+
+extern void solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded);
+extern int solver_check_cleandeps_mistakes(Solver *solv);
+
+
+#define ISSIMPLEDEP(pool, dep) (!ISRELDEP(dep) || GETRELDEP(pool, dep)->flags < 8)
+
+static inline int
+solver_dep_possible(Solver *solv, Id dep, Map *m)
+{
+  Pool *pool = solv->pool;
+  Id p, pp;
+
+  if (!ISSIMPLEDEP(pool, dep))
+    return solver_dep_possible_slow(solv, dep, m);
+  FOR_PROVIDES(p, pp, dep)
+    {  
+      if (MAPTST(m, p))
+        return 1;
+    }
+  return 0;
+}
 
 static inline int
 solver_dep_fulfilled(Solver *solv, Id dep)
 
 static inline int
 solver_dep_fulfilled(Solver *solv, Id dep)
diff --git a/src/solver_util.c b/src/solver_util.c
new file mode 100644 (file)
index 0000000..bd5d4b4
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 2017, SUSE Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * solver_util.c
+ *
+ * Dependency solver helper functions
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "solver.h"
+#include "solver_private.h"
+#include "bitmap.h"
+#include "pool.h"
+#include "poolarch.h"
+#include "util.h"
+
+
+/*-------------------------------------------------------------------
+ * check if a installed package p is being updated
+ */
+static int
+solver_is_updating(Solver *solv, Id p)
+{
+  /* check if the update rule is true */
+  Pool *pool = solv->pool;
+  Rule *r;
+  Id l, pp;
+  if (solv->decisionmap[p] >= 0)
+    return 0;  /* old package stayed */
+  r = solv->rules + solv->updaterules + (p - solv->installed->start);
+  FOR_RULELITERALS(l, pp, r)
+    if (l > 0 && l != p && solv->decisionmap[l] > 0)
+      return 1;
+  return 0;
+}
+
+/*-------------------------------------------------------------------
+ * handle split provides
+ *
+ * a splitprovides dep looks like
+ *     namespace:splitprovides(pkg REL_WITH path)
+ * and is only true if pkg is installed and contains the specified path.
+ * we also make sure that pkg is selected for an update, otherwise the
+ * update would always be forced onto the user.
+ * Map m is the map used when called from dep_possible.
+ */
+int
+solver_splitprovides(Solver *solv, Id dep, Map *m)
+{
+  Pool *pool = solv->pool;
+  Id p, pp;
+  Reldep *rd;
+  Solvable *s;
+
+  if (!solv->dosplitprovides || !solv->installed)
+    return 0;
+  if (!ISRELDEP(dep))
+    return 0;
+  rd = GETRELDEP(pool, dep);
+  if (rd->flags != REL_WITH)
+    return 0;
+  /*
+   * things are a bit tricky here if pool->addedprovides == 1, because most split-provides are in
+   * a non-standard location. If we simply call pool_whatprovides, we'll drag in the complete
+   * file list. Instead we rely on pool_addfileprovides ignoring the addfileprovidesfiltered flag
+   * for installed packages and check the lazywhatprovidesq (ignoring the REL_WITH part, but
+   * we filter the package name further down anyway).
+   */
+  if (pool->addedfileprovides == 1 && !ISRELDEP(rd->evr) && !pool->whatprovides[rd->evr])
+    pp = pool_searchlazywhatprovidesq(pool, rd->evr);
+  else
+    pp = pool_whatprovides(pool, dep);
+  while ((p = pool->whatprovidesdata[pp++]) != 0)
+    {
+      /* here we have packages that provide the correct name and contain the path,
+       * now do extra filtering */
+      s = pool->solvables + p;
+      if (s->repo != solv->installed || s->name != rd->name)
+       continue;
+      /* check if the package is updated. if m is set, we're called from dep_possible */
+      if (m || solver_is_updating(solv, p))
+       return 1;
+    }
+  return 0;
+}
+
+int
+solver_dep_possible_slow(Solver *solv, Id dep, Map *m)
+{
+  Pool *pool = solv->pool;
+  Id p, pp;
+
+  if (ISRELDEP(dep))
+    {
+      Reldep *rd = GETRELDEP(pool, dep);
+      if (rd->flags >= 8)
+         {
+          if (rd->flags == REL_COND || rd->flags == REL_UNLESS)
+            return 1;
+          if (rd->flags == REL_AND)
+            {
+              if (!solver_dep_possible_slow(solv, rd->name, m))
+                return 0;
+              return solver_dep_possible_slow(solv, rd->evr, m);
+            }
+          if (rd->flags == REL_OR)
+            {
+              if (solver_dep_possible_slow(solv, rd->name, m))
+                return 1;
+              return solver_dep_possible_slow(solv, rd->evr, m);
+            }
+          if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
+            return solver_splitprovides(solv, rd->evr, m);
+        }
+    }
+  FOR_PROVIDES(p, pp, dep)
+    {
+      if (MAPTST(m, p))
+        return 1;
+    }
+  return 0;
+}
+
+int
+solver_dep_fulfilled_cplx(Solver *solv, Reldep *rd)
+{
+  Pool *pool = solv->pool;
+  if (rd->flags == REL_COND)
+    {
+      if (ISRELDEP(rd->evr))
+       {
+         Reldep *rd2 = GETRELDEP(pool, rd->evr);
+         if (rd2->flags == REL_ELSE)
+           {
+             if (solver_dep_fulfilled(solv, rd2->name))
+               return solver_dep_fulfilled(solv, rd->name);
+             return solver_dep_fulfilled(solv, rd2->evr);
+           }
+       }
+      if (solver_dep_fulfilled(solv, rd->name))
+       return 1;
+      return !solver_dep_fulfilled(solv, rd->evr);
+    }
+  if (rd->flags == REL_UNLESS)
+    {
+      if (ISRELDEP(rd->evr))
+       {
+         Reldep *rd2 = GETRELDEP(pool, rd->evr);
+         if (rd2->flags == REL_ELSE)
+           {
+             if (!solver_dep_fulfilled(solv, rd2->name))
+               return solver_dep_fulfilled(solv, rd->name);
+             return solver_dep_fulfilled(solv, rd2->evr);
+           }
+       }
+      if (!solver_dep_fulfilled(solv, rd->name))
+       return 0;
+      return !solver_dep_fulfilled(solv, rd->evr);
+    }
+  if (rd->flags == REL_AND)
+    {
+      if (!solver_dep_fulfilled(solv, rd->name))
+       return 0;
+      return solver_dep_fulfilled(solv, rd->evr);
+    }
+  if (rd->flags == REL_OR)
+    {
+      if (solver_dep_fulfilled(solv, rd->name))
+       return 1;
+      return solver_dep_fulfilled(solv, rd->evr);
+    }
+  return 0;
+}
+
+
+/* mirrors solver_dep_fulfilled, but returns 2 if a new package
+ * was involved */
+static int
+solver_dep_fulfilled_alreadyinstalled(Solver *solv, Id dep)
+{
+  Pool *pool = solv->pool;
+  Id p, pp;
+  int r;
+
+  if (ISRELDEP(dep))
+    {
+      Reldep *rd = GETRELDEP(pool, dep);
+      if (rd->flags == REL_COND)
+       {
+         int r1, r2;
+         if (ISRELDEP(rd->evr))
+           {
+             Reldep *rd2 = GETRELDEP(pool, rd->evr);
+             if (rd2->flags == REL_ELSE)
+               {
+                 r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->name);
+                 if (r1)
+                   {
+                     r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
+                     return r2 && r1 == 2 ? 2 : r2;
+                   }
+                 return solver_dep_fulfilled_alreadyinstalled(solv, rd2->evr);
+               }
+           }
+         r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
+         r2 = !solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
+         if (!r1 && !r2)
+           return 0;
+          return r1 == 2 ? 2 : 1;
+       }
+      if (rd->flags == REL_UNLESS)
+       {
+         int r1, r2;
+         if (ISRELDEP(rd->evr))
+           {
+             Reldep *rd2 = GETRELDEP(pool, rd->evr);
+             if (rd2->flags == REL_ELSE)
+               {
+                 r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->name);
+                 if (r1)
+                   {
+                     r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->evr);
+                     return r2 && r1 == 2 ? 2 : r2;
+                   }
+                 return solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
+               }
+           }
+         /* A AND NOT(B) */
+         r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
+         r2 = !solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
+         if (!r1 || !r2)
+           return 0;
+          return r1 == 2 ? 2 : 1;
+       }
+      if (rd->flags == REL_AND)
+        {
+         int r2, r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
+          if (!r1)
+            return 0;
+         r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
+         if (!r2)
+           return 0;
+          return r1 == 2 || r2 == 2 ? 2 : 1;
+        }
+      if (rd->flags == REL_OR)
+       {
+         int r2, r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
+         r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
+         if (!r1 && !r2)
+           return 0;
+          return r1 == 2 || r2 == 2 ? 2 : 1;
+       }
+      if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
+        return solver_splitprovides(solv, rd->evr, 0) ? 2 : 0;
+      if (rd->flags == REL_NAMESPACE && solv->installsuppdepq)
+       {
+         Queue *q = solv->installsuppdepq;
+         int i;
+         for (i = 0; i < q->count; i++)
+           if (q->elements[i] == dep || q->elements[i] == rd->name)
+             return 2;
+       }
+    }
+  r = 0;
+  FOR_PROVIDES(p, pp, dep)
+    if (solv->decisionmap[p] > 0)
+      {
+       Solvable *s = pool->solvables + p;
+       if (s->repo && s->repo != solv->installed)
+         return 2;
+        r = 1;
+      }
+  return r;
+}
+
+int
+solver_is_supplementing_alreadyinstalled(Solver *solv, Solvable *s)
+{
+  Id sup, *supp;
+  supp = s->repo->idarraydata + s->supplements;
+  while ((sup = *supp++) != 0)
+    if (solver_dep_fulfilled_alreadyinstalled(solv, sup) == 2)
+      return 1;
+  return 0;
+}
+/*
+ * add all installed packages that package p obsoletes to Queue q.
+ * Package p is not installed. Also, we know that if
+ * solv->keepexplicitobsoletes is not set, p is not in the multiversion map.
+ * Entries may get added multiple times.
+ */
+static void
+solver_add_obsoleted(Solver *solv, Id p, Queue *q)
+{
+  Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
+  Id p2, pp2;
+  Solvable *s = pool->solvables + p;
+  Id obs, *obsp;
+  Id lastp2 = 0;
+
+  if (!solv->keepexplicitobsoletes || !(solv->multiversion.size && MAPTST(&solv->multiversion, p)))
+    {
+      FOR_PROVIDES(p2, pp2, s->name)
+        {
+          Solvable *ps = pool->solvables + p2;
+          if (ps->repo != installed)
+            continue;
+          if (!pool->implicitobsoleteusesprovides && ps->name != s->name)
+            continue;
+          if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
+            continue;
+          queue_push(q, p2);
+          lastp2 = p2;
+        }
+    }
+  if (!s->obsoletes)
+    return;
+  obsp = s->repo->idarraydata + s->obsoletes;
+  while ((obs = *obsp++) != 0)
+    FOR_PROVIDES(p2, pp2, obs)
+      {
+        Solvable *ps = pool->solvables + p2;
+        if (ps->repo != installed)
+          continue;
+        if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
+          continue;
+        if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
+          continue;
+        if (p2 == lastp2)
+          continue;
+        queue_push(q, p2);
+        lastp2 = p2;
+      }
+}
+
+/*
+ * Call solver_add_obsoleted and intersect the result with the
+ * elements in Queue q starting at qstart.
+ * Assumes that it's the first call if qstart == q->count.
+ * May use auxillary map m for the intersection process, all
+ * elements of q starting at qstart must have their bit cleared.
+ * (This is also true after the function returns.)
+ * (See solver_add_obsoleted for limitations of the package p)
+ */
+void
+solver_intersect_obsoleted(Solver *solv, Id p, Queue *q, int qstart, Map *m)
+{
+  int i, j;
+  int qcount = q->count;
+
+  solver_add_obsoleted(solv, p, q);
+  if (qcount == qstart)
+    return;     /* first call */
+  if (qcount == q->count)
+    j = qstart;
+  else if (qcount == qstart + 1)
+    {
+      /* easy if there's just one element */
+      j = qstart;
+      for (i = qcount; i < q->count; i++)
+        if (q->elements[i] == q->elements[qstart])
+          {
+            j++;        /* keep the element */
+            break;
+          }
+    }
+  else if (!m || (!m->size && q->count - qstart <= 8))
+    {
+      /* faster than a map most of the time */
+      int k;
+      for (i = j = qstart; i < qcount; i++)
+        {
+          Id ip = q->elements[i];
+          for (k = qcount; k < q->count; k++)
+            if (q->elements[k] == ip)
+              {
+                q->elements[j++] = ip;
+                break;
+              }
+        }
+    }
+  else
+    {
+      /* for the really pathologic cases we use the map */
+      Repo *installed = solv->installed;
+      if (!m->size)
+        map_init(m, installed->end - installed->start);
+      for (i = qcount; i < q->count; i++)
+        MAPSET(m, q->elements[i] - installed->start);
+      for (i = j = qstart; i < qcount; i++)
+        if (MAPTST(m, q->elements[i] - installed->start))
+          {
+            MAPCLR(m, q->elements[i] - installed->start);
+            q->elements[j++] = q->elements[i];
+          }
+    }
+  queue_truncate(q, j);
+}
+
index 7002434..afebcc4 100644 (file)
@@ -13,7 +13,8 @@ system i686 rpm system
 
 # check untargeted
 job distupgrade name A [cleandeps]
 
 # check untargeted
 job distupgrade name A [cleandeps]
-result transaction,problems <inline>
+result transaction,problems,cleandeps <inline>
+#>cleandeps B1-1-1.noarch@system
 #>erase B1-1-1.noarch@system
 #>install B2-1-1.noarch@test
 #>upgrade A-1-1.noarch@system A-2-1.noarch@test
 #>erase B1-1-1.noarch@system
 #>install B2-1-1.noarch@test
 #>upgrade A-1-1.noarch@system A-2-1.noarch@test
@@ -21,7 +22,8 @@ result transaction,problems <inline>
 # check targeted
 nextjob
 job distupgrade name A = 2 [cleandeps]
 # check targeted
 nextjob
 job distupgrade name A = 2 [cleandeps]
-result transaction,problems <inline>
+result transaction,problems,cleandeps <inline>
+#>cleandeps B1-1-1.noarch@system
 #>erase B1-1-1.noarch@system
 #>install B2-1-1.noarch@test
 #>upgrade A-1-1.noarch@system A-2-1.noarch@test
 #>erase B1-1-1.noarch@system
 #>install B2-1-1.noarch@test
 #>upgrade A-1-1.noarch@system A-2-1.noarch@test
@@ -29,5 +31,15 @@ result transaction,problems <inline>
 # check targeted to 1-2
 nextjob
 job distupgrade name A = 1-2 [cleandeps]
 # check targeted to 1-2
 nextjob
 job distupgrade name A = 1-2 [cleandeps]
-result transaction,problems <inline>
+result transaction,problems,cleandeps <inline>
 #>upgrade A-1-1.noarch@system A-1-2.noarch@test
 #>upgrade A-1-1.noarch@system A-1-2.noarch@test
+
+# check all packages
+nextjob
+job distupgrade all packages [cleandeps]
+result transaction,problems,cleandeps <inline>
+#>cleandeps B1-1-1.noarch@system
+#>erase B1-1-1.noarch@system
+#>install B2-1-1.noarch@test
+#>upgrade A-1-1.noarch@system A-2-1.noarch@test
+
index 3edacb5..e77a7f7 100644 (file)
@@ -9,7 +9,8 @@ repo test 0 testtags <inline>
 #>=Pkg: B2 1 1 noarch
 system i686 rpm system
 job install name A = 2 [cleandeps]
 #>=Pkg: B2 1 1 noarch
 system i686 rpm system
 job install name A = 2 [cleandeps]
-result transaction,problems <inline>
+result transaction,problems,cleandeps <inline>
+#>cleandeps B1-1-1.noarch@system
 #>erase B1-1-1.noarch@system
 #>install B2-1-1.noarch@test
 #>upgrade A-1-1.noarch@system A-2-1.noarch@test
 #>erase B1-1-1.noarch@system
 #>install B2-1-1.noarch@test
 #>upgrade A-1-1.noarch@system A-2-1.noarch@test
index 9bb26d2..5560a28 100644 (file)
@@ -2,6 +2,9 @@ repo system 0 testtags <inline>
 #>=Pkg: A 1 1 noarch
 #>=Req: B1
 #>=Pkg: B1 1 1 noarch
 #>=Pkg: A 1 1 noarch
 #>=Req: B1
 #>=Pkg: B1 1 1 noarch
+#>=Pkg: C 1 1 noarch
+#>=Rec: D
+#>=Pkg: D 1 1 noarch
 repo test 0 testtags <inline>
 #>=Pkg: A 1 2 noarch
 #>=Req: B1
 repo test 0 testtags <inline>
 #>=Pkg: A 1 2 noarch
 #>=Req: B1
@@ -9,11 +12,15 @@ repo test 0 testtags <inline>
 #>=Req: B2 = 1
 #>=Pkg: B1 1 1 noarch
 #>=Pkg: B2 1 1 noarch
 #>=Req: B2 = 1
 #>=Pkg: B1 1 1 noarch
 #>=Pkg: B2 1 1 noarch
+#>=Pkg: C 1 1 noarch
+#>=Rec: D
 system i686 rpm system
 
 # check untargeted
 job update name A [cleandeps]
 system i686 rpm system
 
 # check untargeted
 job update name A [cleandeps]
-result transaction,problems <inline>
+job update name C [cleandeps]
+result transaction,problems,cleandeps <inline>
+#>cleandeps B1-1-1.noarch@system
 #>erase B1-1-1.noarch@system
 #>install B2-1-1.noarch@test
 #>upgrade A-1-1.noarch@system A-2-1.noarch@test
 #>erase B1-1-1.noarch@system
 #>install B2-1-1.noarch@test
 #>upgrade A-1-1.noarch@system A-2-1.noarch@test
@@ -21,7 +28,8 @@ result transaction,problems <inline>
 # check targeted
 nextjob
 job update name A = 2 [cleandeps]
 # check targeted
 nextjob
 job update name A = 2 [cleandeps]
-result transaction,problems <inline>
+result transaction,problems,cleandeps <inline>
+#>cleandeps B1-1-1.noarch@system
 #>erase B1-1-1.noarch@system
 #>install B2-1-1.noarch@test
 #>upgrade A-1-1.noarch@system A-2-1.noarch@test
 #>erase B1-1-1.noarch@system
 #>install B2-1-1.noarch@test
 #>upgrade A-1-1.noarch@system A-2-1.noarch@test
@@ -29,5 +37,14 @@ result transaction,problems <inline>
 # check targeted to 1-2
 nextjob
 job update name A = 1-2 [cleandeps]
 # check targeted to 1-2
 nextjob
 job update name A = 1-2 [cleandeps]
-result transaction,problems <inline>
+result transaction,problems,cleandeps <inline>
 #>upgrade A-1-1.noarch@system A-1-2.noarch@test
 #>upgrade A-1-1.noarch@system A-1-2.noarch@test
+
+# check all packages
+nextjob
+job update all packages [cleandeps]
+result transaction,problems,cleandeps <inline>
+#>cleandeps B1-1-1.noarch@system
+#>erase B1-1-1.noarch@system
+#>install B2-1-1.noarch@test
+#>upgrade A-1-1.noarch@system A-2-1.noarch@test
index 8be3190..97eb20f 100644 (file)
@@ -12,17 +12,18 @@ system i686 * system
 job multiversion name a
 job distupgrade all packages
 result transaction,problems <inline>
 job multiversion name a
 job distupgrade all packages
 result transaction,problems <inline>
-#>problem 251f1f35 info nothing provides c needed by a-2-1.i686
-#>problem 251f1f35 solution 2f2d254c allow a-1-1.i686@system
+#>problem fc3d647e info nothing provides c needed by a-2-1.i686
+#>problem fc3d647e solution 179b72ed allow a-1-1.i686@system
+#>problem fc3d647e solution e5fc66c9 erase a-1-1.i686@system
 
 nextjob
 
 job multiversion name a
 job distupgrade repo available
 result transaction,problems <inline>
 
 nextjob
 
 job multiversion name a
 job distupgrade repo available
 result transaction,problems <inline>
-#>erase a-1-1.i686@system
-#>problem 251f1f35 info nothing provides c needed by a-2-1.i686
-#>problem 251f1f35 solution 2f2d254c allow a-1-1.i686@system
+#>problem fc3d647e info nothing provides c needed by a-2-1.i686
+#>problem fc3d647e solution 179b72ed allow a-1-1.i686@system
+#>problem fc3d647e solution e5fc66c9 erase a-1-1.i686@system
 
 ### same with keeporphans
 
 
 ### same with keeporphans
 
index f500d9b..8231561 100644 (file)
@@ -9,7 +9,8 @@ system i686 * system
 solverflags !dupallowarchchange
 job distupgrade all packages
 result transaction,problems <inline>
 solverflags !dupallowarchchange
 job distupgrade all packages
 result transaction,problems <inline>
-#>problem c43b1300 info problem with installed package a-1-1.i686
-#>problem c43b1300 solution c43b1300 replace a-1-1.i686@system a-2-1.i586@available
+#>problem 7724e627 info problem with installed package a-1-1.i686
+#>problem 7724e627 solution 25ae2253 allow a-1-1.i686@system
+#>problem 7724e627 solution 2cf4745c replace a-1-1.i686@system a-2-1.i586@available
 #>upgrade a-1-1.i686@system a-2-1.i586@available
 #>upgrade b-1-1.i686@system b-2-1.i686@available
 #>upgrade a-1-1.i686@system a-2-1.i586@available
 #>upgrade b-1-1.i686@system b-2-1.i686@available
index 6c04348..361649a 100644 (file)
@@ -1,6 +1,6 @@
 # test dup with orphaned packages
 #
 # test dup with orphaned packages
 #
-# part 3: a is not really an orphan, but cannott be downgraded
+# part 3: a is not really an orphan, but cannot be downgraded
 #
 
 repo system 0 testtags <inline>
 #
 
 repo system 0 testtags <inline>
@@ -16,8 +16,9 @@ job distupgrade all packages
 result transaction,problems <inline>
 #>downgrade a-1-1.i686@system a-1-0.i686@available
 #>upgrade b-1-1.i686@system b-2-1.i686@available
 result transaction,problems <inline>
 #>downgrade a-1-1.i686@system a-1-0.i686@available
 #>upgrade b-1-1.i686@system b-2-1.i686@available
-#>problem c43b1300 info problem with installed package a-1-1.i686
-#>problem c43b1300 solution c43b1300 replace a-1-1.i686@system a-1-0.i686@available
+#>problem 7724e627 info problem with installed package a-1-1.i686
+#>problem 7724e627 solution 25ae2253 allow a-1-1.i686@system
+#>problem 7724e627 solution 2cf4745c replace a-1-1.i686@system a-1-0.i686@available
 
 nextjob
 
 
 nextjob
 
@@ -26,8 +27,9 @@ job distupgrade repo available
 result transaction,problems <inline>
 #>downgrade a-1-1.i686@system a-1-0.i686@available
 #>upgrade b-1-1.i686@system b-2-1.i686@available
 result transaction,problems <inline>
 #>downgrade a-1-1.i686@system a-1-0.i686@available
 #>upgrade b-1-1.i686@system b-2-1.i686@available
-#>problem c43b1300 info problem with installed package a-1-1.i686
-#>problem c43b1300 solution c43b1300 replace a-1-1.i686@system a-1-0.i686@available
+#>problem 7724e627 info problem with installed package a-1-1.i686
+#>problem 7724e627 solution 25ae2253 allow a-1-1.i686@system
+#>problem 7724e627 solution 2cf4745c replace a-1-1.i686@system a-1-0.i686@available
 
 ### keeporphans
 
 
 ### keeporphans
 
@@ -38,8 +40,9 @@ job distupgrade all packages
 result transaction,problems <inline>
 #>downgrade a-1-1.i686@system a-1-0.i686@available
 #>upgrade b-1-1.i686@system b-2-1.i686@available
 result transaction,problems <inline>
 #>downgrade a-1-1.i686@system a-1-0.i686@available
 #>upgrade b-1-1.i686@system b-2-1.i686@available
-#>problem c43b1300 info problem with installed package a-1-1.i686
-#>problem c43b1300 solution c43b1300 replace a-1-1.i686@system a-1-0.i686@available
+#>problem 7724e627 info problem with installed package a-1-1.i686
+#>problem 7724e627 solution 25ae2253 allow a-1-1.i686@system
+#>problem 7724e627 solution 2cf4745c replace a-1-1.i686@system a-1-0.i686@available
 
 nextjob
 
 
 nextjob
 
@@ -48,9 +51,9 @@ job distupgrade repo available
 result transaction,problems <inline>
 #>downgrade a-1-1.i686@system a-1-0.i686@available
 #>upgrade b-1-1.i686@system b-2-1.i686@available
 result transaction,problems <inline>
 #>downgrade a-1-1.i686@system a-1-0.i686@available
 #>upgrade b-1-1.i686@system b-2-1.i686@available
-#>problem c43b1300 info problem with installed package a-1-1.i686
-#>problem c43b1300 solution c43b1300 replace a-1-1.i686@system a-1-0.i686@available
-
+#>problem 7724e627 info problem with installed package a-1-1.i686
+#>problem 7724e627 solution 25ae2253 allow a-1-1.i686@system
+#>problem 7724e627 solution 2cf4745c replace a-1-1.i686@system a-1-0.i686@available
 
 ### same with allowuninstall
 
 
 ### same with allowuninstall
 
index 1ab09e4..a3cdd45 100644 (file)
@@ -40,7 +40,7 @@ result transaction,problems <inline>
 # second check forced to untargeted
 nextjob
 solverflags noautotarget
 # second check forced to untargeted
 nextjob
 solverflags noautotarget
-job distupgrade name A = 2-1
+job update name A = 2-1
 result transaction,problems <inline>
 
 # then targeted to D
 result transaction,problems <inline>
 
 # then targeted to D
index d751246..e66b340 100644 (file)
@@ -79,11 +79,12 @@ main(int argc, char **argv)
   int c;
   int ex = 0;
   const char *list = 0;
   int c;
   int ex = 0;
   const char *list = 0;
+  int list_with_deps = 0;
   FILE *fp;
   const char *p;
 
   queue_init(&solq);
   FILE *fp;
   const char *p;
 
   queue_init(&solq);
-  while ((c = getopt(argc, argv, "vmrhl:s:T:")) >= 0)
+  while ((c = getopt(argc, argv, "vmrhL:l:s:T:")) >= 0)
     {
       switch (c)
       {
     {
       switch (c)
       {
@@ -101,6 +102,11 @@ main(int argc, char **argv)
           break;
         case 'l':
          list = optarg;
           break;
         case 'l':
          list = optarg;
+         list_with_deps = 0;
+          break;
+        case 'L':
+         list = optarg;
+         list_with_deps = 1;
           break;
         case 's':
          if ((p = strchr(optarg, ':')))
           break;
         case 's':
          if ((p = strchr(optarg, ':')))
@@ -122,6 +128,8 @@ main(int argc, char **argv)
     {
       pool = pool_create();
       pool_setdebuglevel(pool, debuglevel);
     {
       pool = pool_create();
       pool_setdebuglevel(pool, debuglevel);
+      /* report all errors */
+      pool_setdebugmask(pool, pool->debugmask | SOLV_ERROR);
 
       fp = fopen(argv[optind], "r");
       if (!fp)
 
       fp = fopen(argv[optind], "r");
       if (!fp)
@@ -153,11 +161,17 @@ main(int argc, char **argv)
            printf("test %d:\n", multijob++);
          if (list)
            {
            printf("test %d:\n", multijob++);
          if (list)
            {
+             Id p = 0;
              int selflags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL|SELECTION_GLOB|SELECTION_FLAT;
              if (*list == '/')
                selflags |= SELECTION_FILELIST;
              queue_empty(&job);
              int selflags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL|SELECTION_GLOB|SELECTION_FLAT;
              if (*list == '/')
                selflags |= SELECTION_FILELIST;
              queue_empty(&job);
-             selection_make(pool, &job, list, selflags);
+             if (list_with_deps)
+               p = testcase_str2solvid(pool, list);
+             if (p)
+               queue_push2(&job, SOLVER_SOLVABLE, p);
+             else
+               selection_make(pool, &job, list, selflags);
              if (!job.elements)
                printf("No match\n");
              else
              if (!job.elements)
                printf("No match\n");
              else
@@ -167,7 +181,34 @@ main(int argc, char **argv)
                  queue_init(&q);
                  selection_solvables(pool, &job, &q);
                  for (i = 0; i < q.count; i++)
                  queue_init(&q);
                  selection_solvables(pool, &job, &q);
                  for (i = 0; i < q.count; i++)
-                   printf("  - %s\n", testcase_solvid2str(pool, q.elements[i]));
+                   {
+                     printf("  - %s\n", testcase_solvid2str(pool, q.elements[i]));
+                     if (list_with_deps)
+                       {
+                         int j, k;
+                         const char *vendor;
+                         static Id deps[] = {
+                           SOLVABLE_PROVIDES, SOLVABLE_REQUIRES, SOLVABLE_CONFLICTS, SOLVABLE_OBSOLETES,
+                           SOLVABLE_RECOMMENDS, SOLVABLE_SUGGESTS, SOLVABLE_SUPPLEMENTS, SOLVABLE_ENHANCES,
+                           SOLVABLE_PREREQ_IGNOREINST,
+                           0
+                         };
+                         vendor = pool_lookup_str(pool, q.elements[i], SOLVABLE_VENDOR);
+                         if (vendor)
+                           printf("    %s: %s\n", pool_id2str(pool, SOLVABLE_VENDOR), vendor);
+                         for (j = 0; deps[j]; j++)
+                           {
+                             Queue dq;
+                             queue_init(&dq);
+                             pool_lookup_idarray(pool, q.elements[i], deps[j], &dq);
+                             if (dq.count)
+                               printf("    %s:\n", pool_id2str(pool, deps[j]));
+                             for (k = 0; k < dq.count; k++)
+                               printf("      %s\n", pool_dep2str(pool, dq.elements[k]));
+                             queue_free(&dq);
+                           }
+                       }
+                   }
                  queue_free(&q);
                }
            }
                  queue_free(&q);
                }
            }