Imported Upstream version 0.6.31 01/194201/1 upstream/0.6.31
authorDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 30 Nov 2018 03:41:03 +0000 (12:41 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 30 Nov 2018 03:41:03 +0000 (12:41 +0900)
Change-Id: I6009e95508e9937392b83a003dab4f30cf3a9b1b
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
43 files changed:
CMakeLists.txt
NEWS
TODO_1.0
VERSION.cmake
bindings/solv.i
doc/gen/libsolv-bindings.3
doc/gen/libsolv-pool.3
doc/libsolv-bindings.txt
doc/libsolv-pool.txt
examples/p5solv
examples/solv/repoinfo_type_rpmmd.c
examples/solv/repoinfo_type_susetags.c
examples/solv/solv.c
ext/repo_rpmdb.c
ext/repo_rpmdb_bdb.h [new file with mode: 0644]
ext/repo_rpmdb_librpm.h [new file with mode: 0644]
ext/solv_xfopen.c
ext/testcase.c
ext/testcase.h
package/libsolv.changes
package/libsolv.spec.in
src/CMakeLists.txt
src/bitmap.c
src/bitmap.h
src/libsolv.ver
src/pool.c
src/pool.h
src/problems.c
src/problems.h
src/repo.h
src/rules.c
src/selection.c
src/selection.h
src/solver.c
src/solver.h
src/userinstalled.c [new file with mode: 0644]
test/testcases/forcebest/forcebest_in.t
test/testcases/selection/selection_canon_rpm.t [new file with mode: 0644]
test/testcases/selection/selection_filelist.t [new file with mode: 0644]
test/testcases/selection/selection_matchdeps.t [new file with mode: 0644]
test/testcases/selection/selection_name.t [new file with mode: 0644]
test/testcases/selection/selection_provides.t [new file with mode: 0644]
tools/testsolv.c

index 605ab76..2557f0a 100644 (file)
@@ -16,6 +16,8 @@ OPTION (ENABLE_RPMDB "Build with rpm database support?" OFF)
 OPTION (ENABLE_RPMPKG "Build with rpm package support?" OFF)
 OPTION (ENABLE_PUBKEY "Build with pubkey support?" OFF)
 OPTION (ENABLE_RPMDB_BYRPMHEADER "Build with rpmdb Header support?" OFF)
+OPTION (ENABLE_RPMDB_LIBRPM "Use librpm to access the rpm database?" OFF)
+OPTION (ENABLE_RPMPKG_LIBRPM "Use librpm to access rpm header information?" OFF)
 OPTION (ENABLE_RPMMD "Build with rpmmd repository support?" OFF)
 OPTION (ENABLE_SUSEREPO "Build with suse repository support?" OFF)
 OPTION (ENABLE_COMPS "Build with fedora comps support?" OFF)
@@ -201,7 +203,7 @@ SET (ENABLE_RPMPKG ON)
 ENDIF (ENABLE_RPMDB)
 
 INCLUDE (CheckIncludeFile)
-IF (ENABLE_RPMDB)
+IF (ENABLE_RPMDB OR ENABLE_RPMPKG_LIBRPM)
   FIND_LIBRARY (RPMDB_LIBRARY NAMES rpmdb)
 
   IF (NOT RPMDB_LIBRARY)
@@ -222,18 +224,20 @@ IF (ENABLE_RPMDB)
 
   # check if rpm contains a bundled berkeley db
   CHECK_INCLUDE_FILE(rpm/db.h HAVE_RPM_DB_H)
-  IF (NOT HAVE_RPM_DB_H)
-    FIND_LIBRARY (DB_LIBRARY NAMES db)
-    IF (DB_LIBRARY)
-      SET (RPMDB_LIBRARY ${DB_LIBRARY} ${RPMDB_LIBRARY})
-    ENDIF (DB_LIBRARY)
-    IF (DB_INCLUDE_DIR)
-      INCLUDE_DIRECTORIES (${DB_INCLUDE_DIR})
-    ENDIF (DB_INCLUDE_DIR)
-  ENDIF (NOT HAVE_RPM_DB_H)
+  IF (NOT ENABLE_RPMDB_LIBRPM)
+    IF (NOT HAVE_RPM_DB_H)
+      FIND_LIBRARY (DB_LIBRARY NAMES db)
+      IF (DB_LIBRARY)
+        SET (RPMDB_LIBRARY ${DB_LIBRARY} ${RPMDB_LIBRARY})
+      ENDIF (DB_LIBRARY)
+      IF (DB_INCLUDE_DIR)
+        INCLUDE_DIRECTORIES (${DB_INCLUDE_DIR})
+      ENDIF (DB_INCLUDE_DIR)
+    ENDIF (NOT HAVE_RPM_DB_H)
+  ENDIF (NOT ENABLE_RPMDB_LIBRPM)
   INCLUDE (CheckLibraryExists)
   CHECK_LIBRARY_EXISTS(rpmio pgpDigGetParams "" HAVE_PGPDIGGETPARAMS)
-ENDIF (ENABLE_RPMDB)
+ENDIF (ENABLE_RPMDB OR ENABLE_RPMPKG_LIBRPM)
 
 IF (ENABLE_PUBKEY)
   SET (ENABLE_PGPVRFY ON)
@@ -283,7 +287,8 @@ FOREACH (VAR
 ENDFOREACH (VAR)
 
 FOREACH (VAR
-  ENABLE_RPMDB ENABLE_RPMPKG ENABLE_PUBKEY ENABLE_RPMMD ENABLE_RPMDB_BYRPMHEADER
+  ENABLE_RPMDB ENABLE_RPMPKG ENABLE_PUBKEY ENABLE_RPMMD
+  ENABLE_RPMPKG_LIBRPM ENABLE_RPMDB_LIBRPM ENABLE_RPMDB_BYRPMHEADER
   ENABLE_SUSEREPO ENABLE_COMPS ENABLE_TESTCASE_HELIXREPO
   ENABLE_HELIXREPO ENABLE_MDKREPO ENABLE_ARCHREPO ENABLE_DEBIAN ENABLE_HAIKU
   ENABLE_ZLIB_COMPRESSION ENABLE_LZMA_COMPRESSION ENABLE_BZIP2_COMPRESSION
diff --git a/NEWS b/NEWS
index e3f7845..47a699d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,22 @@
 This file contains the major changes between
 libsolv versions:
 
+Version 0.6.31
+- new configuration options:
+  * ENABLE_RPMDB_LIBRPM: use librpm to read the package
+    database
+  * ENABLE_RPMPKG_LIBRPM: use librpm to access information
+    from package headers
+- new features:
+  * new pool_set_whatprovides function to manually change
+    the whatprovides data
+  * new selection_subtract function to remove packages of
+    one selection from another selection
+  * new selection flags SELECTION_FILTER,
+    SELECTION_WITH_DISABLED and SELECTION_WITH_BADARCH
+  * new map_invertall function to invert a bitmap
+  * new map_clr_at function to clear some bits
+
 Version 0.6.30
 - new features:
   * many fixes and extenstions for cleandeps, e.g.
index 6ecb862..13000fc 100644 (file)
--- a/TODO_1.0
+++ b/TODO_1.0
@@ -9,10 +9,6 @@
 
 - deal with DIRSTR entries having dirid 0 (for source rpms)
 
-- test rich dep rule creation
-
-- clean up rich dep rule code (see perl-BSSolv)
-
 - drop patchcheck
 
 - rename repo2solv.sh to repo2solv (maybe rewrite in C?)
@@ -23,7 +19,7 @@
 
 - write more manpages
 
-- bindings: selections.flags() should be a attribute and not a method
+- bindings: selection.flags() should be a attribute and not a method
 
 - rename repodata_lookup_id_uninternalized to repodata_lookup_id_voidid_uninternalized
   and add a notfound argument
index b047bfc..c303b53 100644 (file)
@@ -49,5 +49,5 @@ SET(LIBSOLVEXT_SOVERSION "0")
 
 SET(LIBSOLV_MAJOR "0")
 SET(LIBSOLV_MINOR "6")
-SET(LIBSOLV_PATCH "30")
+SET(LIBSOLV_PATCH "31")
 
index 6313d9b..add97a2 100644 (file)
@@ -1261,8 +1261,18 @@ typedef struct {
   static const Id SELECTION_GLOB = SELECTION_GLOB;
   static const Id SELECTION_FLAT = SELECTION_FLAT;
   static const Id SELECTION_NOCASE = SELECTION_NOCASE;
+  static const Id SELECTION_SKIP_KIND = SELECTION_SKIP_KIND;
+  static const Id SELECTION_MATCH_DEPSTR = SELECTION_MATCH_DEPSTR;
   static const Id SELECTION_SOURCE_ONLY = SELECTION_SOURCE_ONLY;
   static const Id SELECTION_WITH_SOURCE = SELECTION_WITH_SOURCE;
+  static const Id SELECTION_WITH_DISABLED = SELECTION_WITH_DISABLED;
+  static const Id SELECTION_WITH_BADARCH = SELECTION_WITH_BADARCH;
+  static const Id SELECTION_WITH_ALL = SELECTION_WITH_ALL;
+  static const Id SELECTION_ADD = SELECTION_ADD;
+  static const Id SELECTION_SUBTRACT = SELECTION_SUBTRACT;
+  static const Id SELECTION_FILTER = SELECTION_FILTER;
+  static const Id SELECTION_FILTER_KEEP_IFEMPTY = SELECTION_FILTER_KEEP_IFEMPTY;
+  static const Id SELECTION_FILTER_SWAPPED = SELECTION_FILTER_SWAPPED;
 
   Selection(Pool *pool) {
     Selection *s;
@@ -1284,6 +1294,15 @@ typedef struct {
   bool isempty() {
     return $self->q.count == 0;
   }
+  %newobject clone;
+  Selection *clone(Selection *from, int flags = 0) { 
+    Selection *s;
+    s = solv_calloc(1, sizeof(*s));
+    s->pool = from->pool;
+    s->flags = from->flags;
+    queue_init_clone(&s->q, &from->q);
+    return s;
+  }
   void filter(Selection *lsel) {
     if ($self->pool != lsel->pool)
       queue_empty(&$self->q);
@@ -1300,6 +1319,27 @@ typedef struct {
   void add_raw(Id how, Id what) {
     queue_push2(&$self->q, how, what);
   }
+  void subtract(Selection *lsel) {
+    if ($self->pool == lsel->pool)
+      selection_subtract($self->pool, &$self->q, &lsel->q);
+  }
+  
+  void select(const char *name, int flags) {
+    if ((flags & SELECTION_MODEBITS) == 0)
+      flags |= SELECTION_FILTER | SELECTION_WITH_ALL;
+    $self->flags = selection_make($self->pool, &$self->q, name, flags);
+  }
+  void matchdeps(const char *name, int flags, Id keyname, Id marker = -1) {
+    if ((flags & SELECTION_MODEBITS) == 0)
+      flags |= SELECTION_FILTER | SELECTION_WITH_ALL;
+    $self->flags = selection_make_matchdeps($self->pool, &$self->q, name, flags, keyname, marker);
+  }
+  void matchdepid(DepId dep, int flags, Id keyname, Id marker = -1) {
+    if ((flags & SELECTION_MODEBITS) == 0)
+      flags |= SELECTION_FILTER | SELECTION_WITH_ALL;
+    $self->flags = selection_make_matchdepid($self->pool, &$self->q, dep, flags, keyname, marker);
+  }
+
   %typemap(out) Queue jobs Queue2Array(Job *, 2, new_Job(arg1->pool, id, idp[1]));
   %newobject jobs;
   Queue jobs(int flags) {
@@ -1848,6 +1888,16 @@ typedef struct {
     return pool_queuetowhatprovides($self, &q);
   }
 
+  void set_namespaceproviders(DepId ns, DepId evr, bool value=1) {
+    Id dep = pool_rel2id($self, ns, evr, REL_NAMESPACE, 1);
+    pool_set_whatprovides($self, dep, value ? 2 : 1);
+  }
+
+  void flush_namespaceproviders(DepId ns, DepId evr) {
+    pool_flush_namespaceproviders($self, ns, evr);
+  }
+
+
   %typemap(out) Queue whatmatchesdep Queue2Array(XSolvable *, 1, new_XSolvable(arg1, id));
   %newobject whatmatchesdep;
   Queue whatmatchesdep(Id keyname, DepId dep, Id marker = -1) {
@@ -1893,6 +1943,20 @@ typedef struct {
     return sel;
   }
 
+  %newobject matchdeps;
+  Selection *matchdeps(const char *name, int flags, Id keyname, Id marker = -1) {
+    Selection *sel = new_Selection($self);
+    sel->flags = selection_make_matchdeps($self, &sel->q, name, flags, keyname, marker);
+    return sel;
+  }
+
+  %newobject matchdepid;
+  Selection *matchdepid(DepId dep, int flags, Id keyname, Id marker = -1) {
+    Selection *sel = new_Selection($self);
+    sel->flags = selection_make_matchdepid($self, &sel->q, dep, flags, keyname, marker);
+    return sel;
+  }
+
   void setpooljobs_helper(Queue jobs) {
     queue_free(&$self->pooljobs);
     queue_init_clone(&$self->pooljobs, &jobs);
index 7350224..7ea859f 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: Libsolv-Bindings
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\"      Date: 09/07/2017
+.\"      Date: 01/18/2018
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "LIBSOLV\-BINDINGS" "3" "09/07/2017" "libsolv" "LIBSOLV"
+.TH "LIBSOLV\-BINDINGS" "3" "01/18/2018" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -881,6 +881,36 @@ my \fI$offset\fR \fB=\fR \fI$pool\fR\fB\->towhatprovides(\e\fR\fI@ids\fR\fB)\fR;
 .RS 4
 .\}
 .nf
+\fBvoid set_namespaceproviders(DepId\fR \fIns\fR\fB, DepId\fR \fIevr\fR\fB, bool\fR \fIvalue\fR \fB= 1)\fR
+\fI$pool\fR\fB\->set_namespaceproviders(\fR\fI$ns\fR\fB,\fR \fI$evr\fR\fB, 1)\fR;
+\fIpool\fR\fB\&.set_namespaceproviders(\fR\fIns\fR\fB,\fR \fIevr\fR\fB,\fR \fITrue\fR\fB)\fR
+\fIpool\fR\fB\&.set_namespaceproviders(\fR\fIns\fR\fB,\fR \fIevr\fR\fB,\fR \fItrue\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Manually set an namespace provides entry in the whatprovides index\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid flush_namespaceproviders(DepId\fR \fIns\fR\fB, DepId\fR \fIevr\fR\fB)\fR
+\fI$pool\fR\fB\->flush_namespaceproviders(\fR\fI$ns\fR\fB,\fR \fI$evr\fR\fB)\fR;
+\fI$pool\fR\fB\&.flush_namespaceproviders(\fR\fIns\fR\fB,\fR \fIevr\fR\fB)\fR
+\fI$pool\fR\fB\&.flush_namespaceproviders(\fR\fIns\fR\fB,\fR \fIevr\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Flush the cache of all namespacprovudes matching the specified namespace dependency\&. You can use zero as a wildcard argument\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
 \fBbool isknownarch(DepId\fR \fIid\fR\fB)\fR
 my \fI$bool\fR \fB=\fR \fI$pool\fR\fB\->isknownarch(\fR\fI$id\fR\fB)\fR;
 \fIbool\fR \fB=\fR \fIpool\fR\fB\&.isknownarch(\fR\fIid\fR\fB)\fR
@@ -971,6 +1001,36 @@ Create a selection by matching packages against the specified string\&. See the
 .RS 4
 .\}
 .nf
+\fBSelection matchdeps(const char *\fR\fIname\fR\fB, int\fR \fIflags\fR\fB, Id\fR \fIkeyname\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR
+my \fI$sel\fR \fB=\fR \fI$pool\fR\fB\->matchdeps(\fR\fI$name\fR\fB,\fR \fI$flags\fR\fB,\fR \fI$keyname\fR\fB)\fR;
+\fIsel\fR \fB=\fR \fIpool\fR\fB\&.matchdeps(\fR\fIname\fR\fB,\fR \fIflags\fR\fB,\fR \fIkeyname\fR\fB)\fR
+\fIsel\fR \fB=\fR \fIpool\fR\fB\&.matchdeps(\fR\fIname\fR\fB,\fR \fIflags\fR\fB,\fR \fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a selection by matching package dependencies against the specified string\&. This can be used if you want to match other dependency types than "provides"\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSelection matchdepid(DepId\fR \fIdep\fR\fB, int\fR \fIflags\fR\fB, Id\fR \fIkeyname\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR
+my \fI$sel\fR \fB=\fR \fI$pool\fR\fB\->matchdepid(\fR\fIdep\fR\fB,\fR \fI$flags\fR\fB,\fR \fI$keyname\fR\fB)\fR;
+\fIsel\fR \fB=\fR \fIpool\fR\fB\&.matchdepid(\fR\fIdep\fR\fB,\fR \fIflags\fR\fB,\fR \fIkeyname\fR\fB)\fR
+\fIsel\fR \fB=\fR \fIpool\fR\fB\&.matchdepid(\fR\fIdep\fR\fB,\fR \fIflags\fR\fB,\fR \fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create a selection by matching package dependencies against the specified dependency\&. This may be faster than matchdeps and also works with complex dependencies\&. The downside is that you cannot use globs or case insensitive matching\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
 \fBvoid setpooljobs(Jobs *\fR\fIjobs\fR\fB)\fR
 \fI$pool\fR\fB\->setpooljobs(\e\fR\fI@jobs\fR\fB)\fR;
 \fIpool\fR\fB\&.setpooljobs(\fR\fIjobs\fR\fB)\fR
@@ -3008,12 +3068,37 @@ Create the selection by matching the canonical representation of the package\&.
 .PP
 \fBSELECTION_DOTARCH\fR
 .RS 4
-Allow an \(lq\&.<architecture>\(rq suffix when matching names or provides\&.
+Allow an "\&.<architecture>" suffix when matching names or provides\&.
 .RE
 .PP
 \fBSELECTION_REL\fR
 .RS 4
-Allow the specification of a relation when matching names or provides, e\&.g\&. "name >= 1\&.2"\&.
+Allow the specification of a relation when matching names or dependencies, e\&.g\&. "name >= 1\&.2"\&.
+.RE
+.PP
+\fBSELECTION_GLOB\fR
+.RS 4
+Allow glob matching for package names, package provides, and file names\&.
+.RE
+.PP
+\fBSELECTION_NOCASE\fR
+.RS 4
+Ignore case when matching package names, package provides, and file names\&.
+.RE
+.PP
+\fBSELECTION_FLAT\fR
+.RS 4
+Return only one selection element describing the selected packages\&. The default is to create multiple elements for all globbed packages\&. Multiple elements are useful if you want to turn the selection into an install job, in that case you want an install job for every globbed package\&.
+.RE
+.PP
+\fBSELECTION_SKIP_KIND\fR
+.RS 4
+Remove a "packagekind:" prefix from the package names\&.
+.RE
+.PP
+\fBSELECTION_MATCH_DEPSTR\fR
+.RS 4
+When matching dependencies, do a string match on the result of dep2str instead of using the normal dependency intersect algorithm\&.
 .RE
 .PP
 \fBSELECTION_INSTALLED_ONLY\fR
@@ -3031,19 +3116,34 @@ Limit the package search to source packages only\&.
 Extend the package search to also match source packages\&. The default is only to match binary packages\&.
 .RE
 .PP
-\fBSELECTION_GLOB\fR
+\fBSELECTION_WITH_DISABLED\fR
 .RS 4
-Allow glob matching for package names, package provides, and file names\&.
+Extend the package search to also include disabled packages\&.
 .RE
 .PP
-\fBSELECTION_NOCASE\fR
+\fBSELECTION_WITH_BADARCH\fR
 .RS 4
-Ignore case when matching package names, package provides, and file names\&.
+Extend the package search to also include packages that are not installable on the configured architecture\&.
 .RE
 .PP
-\fBSELECTION_FLAT\fR
+\fBSELECTION_WITH_ALL\fR
 .RS 4
-Return only one selection element describing the selected packages\&. The default is to create multiple elements for all globbed packages\&. Multiple elements are useful if you want to turn the selection into an install job, in that case you want an install job for every globbed package\&.
+Shortcut for selecting the three modifiers above\&.
+.RE
+.PP
+\fBSELECTION_ADD\fR
+.RS 4
+Add the result of the match to the current selection instead of replacing it\&.
+.RE
+.PP
+\fBSELECTION_SUBTRACT\fR
+.RS 4
+Remove the result of the match to the current selection instead of replacing it\&.
+.RE
+.PP
+\fBSELECTION_FILTER\fR
+.RS 4
+Intersect the result of the match to the current selection instead of replacing it\&.
 .RE
 .SS "ATTRIBUTES"
 .sp
@@ -3127,6 +3227,21 @@ Build the union of two selections\&. All packages of the other selection will be
 .RS 4
 .\}
 .nf
+\fBvoid subtract(Selection *\fR\fIother\fR\fB)\fR
+\fI$sel\fR\fB\->subtract(\fR\fI$other\fR\fB)\fR;
+\fIsel\fR\fB\&.subtract(\fR\fIother\fR\fB)\fR
+\fIsel\fR\fB\&.subtract(\fR\fIother\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Remove the packages of the other selection from the packages of the selection object\&. Does an in\-place modification\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
 \fBvoid add_raw(Id\fR \fIhow\fR\fB, Id\fR \fIwhat\fR\fB)\fR
 \fI$sel\fR\fB\->add_raw(\fR\fI$how\fR\fB,\fR \fI$what\fR\fB)\fR;
 \fIsel\fR\fB\&.add_raw(\fR\fIhow\fR\fB,\fR \fIwhat\fR\fB)\fR
@@ -3136,7 +3251,7 @@ Build the union of two selections\&. All packages of the other selection will be
 .RE
 .\}
 .sp
-Add a raw element to the selection\&. Check the Job class for information about the how and what parameters\&.
+Add a raw element to the selection\&. Check the Job class for information about the how and what parameters\&. Note that the selection flags are no longer meaningful after the add_raw operation\&.
 .sp
 .if n \{\
 .RS 4
@@ -3172,6 +3287,51 @@ Convert a selection into an array of Solvable objects\&.
 .RS 4
 .\}
 .nf
+\fBvoid select(const char *\fR\fIname\fR\fB, int\fR \fIflags\fR\fB)\fR
+\fI$sel\fR\fB\->select(\fR\fI$name\fR\fB,\fR \fI$flags\fR\fB)\fR;
+\fIsel\fR\fB\&.select(\fR\fIname\fR\fB,\fR \fIflags\fR\fB)\fR
+\fIsel\fR\fB\&.select(\fR\fIname\fR\fB,\fR \fIflags\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Do a select operation and combine the result with the current selection\&. You can choose the desired combination method by using either the SELECTION_ADD, SELECTION_SUBTRACT, or SELECTION_FILTER flag\&. If none of the flags are used, SELECTION_FILTER|SELECTION_WITH_ALL is assumed\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid matchdeps(const char *\fR\fIname\fR\fB, int\fR \fIflags\fR\fB, Id\fR \fIkeyname\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR
+\fI$sel\fR\fB\->matchdeps(\fR\fI$name\fR\fB,\fR \fI$flags\fR\fB,\fR \fI$keyname\fR\fB)\fR;
+\fIsel\fR\fB\&.matchdeps(\fR\fIname\fR\fB,\fR \fIflags\fR\fB,\fR \fIkeyname\fR\fB)\fR
+\fIsel\fR\fB\&.matchdeps(\fR\fIname\fR\fB,\fR \fIflags\fR\fB,\fR \fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Do a matchdeps operation and combine the result with the current selection\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBvoid matchdepid(DepId\fR \fIdep\fR\fB, int\fR \fIflags\fR\fB, Id\fR \fIkeyname\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR
+\fI$sel\fR\fB\->matchdepid(\fR\fI$dep\fR\fB,\fR \fI$flags\fR\fB,\fR \fI$keyname\fR\fB)\fR;
+\fIsel\fR\fB\&.matchdepid(\fR\fIdep\fR\fB,\fR \fIflags\fR\fB,\fR \fIkeyname\fR\fB)\fR
+\fIsel\fR\fB\&.matchdepid(\fR\fIdep\fR\fB,\fR \fIflags\fR\fB,\fR \fIkeyname\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Do a matchdepid operation and combine the result with the current selection\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
 \fB<stringification>\fR
 my \fI$str\fR \fB=\fR \fI$sel\fR\fB\->str\fR;
 \fIstr\fR \fB= str(\fR\fIsel\fR\fB)\fR
index d324f68..ffb8e56 100644 (file)
@@ -1,13 +1,13 @@
 '\" t
 .\"     Title: Libsolv-Pool
 .\"    Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
-.\"      Date: 09/04/2017
+.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
+.\"      Date: 01/18/2018
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "LIBSOLV\-POOL" "3" "09/04/2017" "libsolv" "LIBSOLV"
+.TH "LIBSOLV\-POOL" "3" "01/18/2018" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -956,6 +956,18 @@ Same as pool_addfileprovides, but the added Ids are returned in two Queues, \fIi
 .RS 4
 .\}
 .nf
+\fBvoid pool_set_whatprovides(\fR\fIpool\fR\fB, Id\fR \fIid\fR\fB, Id\fR \fIoffset\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Manually set an entry in the whatprovides index\&. You\(cqll never do this for package dependencies, as those entries are created by calling the pool_createwhatprovides() function\&. But this function is useful for namespace provides if you do not want to use a namespace callback to lazily set the provides\&. The offset argument is a offset in the whatprovides array, thus you can use \(lq1\(rq as a false value and \(lq2\(rq as true value\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
 \fBvoid pool_flush_namespaceproviders(Pool *\fR\fIpool\fR\fB, Id\fR \fIns\fR\fB, Id\fR \fIevr\fR\fB)\fR;
 .fi
 .if n \{\
index 83ae6c2..0d336ce 100644 (file)
@@ -504,6 +504,21 @@ string. See the Dataiterator class for the allowed flags.
 create solver jobs working on a specific set of packages. See the Solver class
 for more information.
 
+       void set_namespaceproviders(DepId ns, DepId evr, bool value = 1)
+       $pool->set_namespaceproviders($ns, $evr, 1);
+       pool.set_namespaceproviders(ns, evr, True)
+       pool.set_namespaceproviders(ns, evr, true)
+
+Manually set an namespace provides entry in the whatprovides index.
+
+       void flush_namespaceproviders(DepId ns, DepId evr)
+        $pool->flush_namespaceproviders($ns, $evr);
+        $pool.flush_namespaceproviders(ns, evr)
+        $pool.flush_namespaceproviders(ns, evr)
+
+Flush the cache of all namespacprovudes matching the specified namespace
+dependency. You can use zero as a wildcard argument.
+
        bool isknownarch(DepId id)
        my $bool = $pool->isknownarch($id);
        bool = pool.isknownarch(id)
@@ -551,6 +566,24 @@ Create a selection by matching packages against the specified string. See the
 Selection class for a list of flags and how to create solver jobs from a
 selection.
 
+       Selection matchdeps(const char *name, int flags, Id keyname, Id marker = -1)
+       my $sel = $pool->matchdeps($name, $flags, $keyname);
+       sel = pool.matchdeps(name, flags, keyname)
+       sel = pool.matchdeps(name, flags, keyname)
+
+Create a selection by matching package dependencies against the specified string.
+This can be used if you want to match other dependency types than "provides".
+
+       Selection matchdepid(DepId dep, int flags, Id keyname, Id marker = -1)
+       my $sel = $pool->matchdepid(dep, $flags, $keyname);
+       sel = pool.matchdepid(dep, flags, keyname)
+       sel = pool.matchdepid(dep, flags, keyname)
+
+Create a selection by matching package dependencies against the specified
+dependency. This may be faster than matchdeps and also works with complex
+dependencies. The downside is that you cannot use globs or case insensitive
+matching.
+
        void setpooljobs(Jobs *jobs)
        $pool->setpooljobs(\@jobs);
        pool.setpooljobs(jobs)
@@ -1682,22 +1715,12 @@ of the package. This is normally a combination of the name,
 the version, and the architecture of a package.
 
 *SELECTION_DOTARCH*::
-Allow an ``.<architecture>'' suffix when matching names or
+Allow an ".<architecture>" suffix when matching names or
 provides.
  
 *SELECTION_REL*::
 Allow the specification of a relation when matching names
-or provides, e.g. "name >= 1.2".
-
-*SELECTION_INSTALLED_ONLY*::
-Limit the package search to installed packages.
-
-*SELECTION_SOURCE_ONLY*::
-Limit the package search to source packages only.
-
-*SELECTION_WITH_SOURCE*::
-Extend the package search to also match source packages. The default is
-only to match binary packages.
+or dependencies, e.g. "name >= 1.2".
 
 *SELECTION_GLOB*::
 Allow glob matching for package names, package provides, and file names.
@@ -1712,6 +1735,42 @@ Multiple elements are useful if you want to turn the selection into
 an install job, in that case you want an install job for every
 globbed package.
 
+*SELECTION_SKIP_KIND*::
+Remove a "packagekind:" prefix from the package names.
+
+*SELECTION_MATCH_DEPSTR*::
+When matching dependencies, do a string match on the result of dep2str
+instead of using the normal dependency intersect algorithm.
+
+*SELECTION_INSTALLED_ONLY*::
+Limit the package search to installed packages.
+
+*SELECTION_SOURCE_ONLY*::
+Limit the package search to source packages only.
+
+*SELECTION_WITH_SOURCE*::
+Extend the package search to also match source packages. The default is
+only to match binary packages.
+
+*SELECTION_WITH_DISABLED*::
+Extend the package search to also include disabled packages.
+
+*SELECTION_WITH_BADARCH*::
+Extend the package search to also include packages that are not installable
+on the configured architecture.
+
+*SELECTION_WITH_ALL*::
+Shortcut for selecting the three modifiers above.
+
+*SELECTION_ADD*::
+Add the result of the match to the current selection instead of replacing it.
+
+*SELECTION_SUBTRACT*::
+Remove the result of the match to the current selection instead of replacing it.
+
+*SELECTION_FILTER*::
+Intersect the result of the match to the current selection instead of replacing it.
+
 === ATTRIBUTES ===
 
        Pool *pool;                             /* read only */
@@ -1761,13 +1820,22 @@ be added to the set of packages of the selection object. Does an in-place
 modification. Note that the selection flags are no longer meaningful after the
 add operation.
 
+       void subtract(Selection *other)
+       $sel->subtract($other);
+       sel.subtract(other)
+       sel.subtract(other)
+
+Remove the packages of the other selection from the packages of the selection
+object. Does an in-place modification.
+
        void add_raw(Id how, Id what)
        $sel->add_raw($how, $what);
        sel.add_raw(how, what)
        sel.add_raw(how, what)
 
 Add a raw element to the selection. Check the Job class for information about
-the how and what parameters.
+the how and what parameters. Note that the selection flags are no longer meaningful
+after the add_raw operation.
 
        Job *jobs(int action)
        my @jobs = $sel->jobs($action);
@@ -1785,6 +1853,30 @@ erase). See the Job class for the action and action modifier constants.
 
 Convert a selection into an array of Solvable objects.
 
+       void select(const char *name, int flags)
+       $sel->select($name, $flags);
+       sel.select(name, flags)
+       sel.select(name, flags)
+
+Do a select operation and combine the result with the current selection. You
+can choose the desired combination method by using either the SELECTION_ADD,
+SELECTION_SUBTRACT, or SELECTION_FILTER flag. If none of the flags are
+used, SELECTION_FILTER|SELECTION_WITH_ALL is assumed.
+
+       void matchdeps(const char *name, int flags, Id keyname, Id marker = -1)
+       $sel->matchdeps($name, $flags, $keyname);
+       sel.matchdeps(name, flags, keyname)
+       sel.matchdeps(name, flags, keyname)
+
+Do a matchdeps operation and combine the result with the current selection.
+
+       void matchdepid(DepId dep, int flags, Id keyname, Id marker = -1)
+       $sel->matchdepid($dep, $flags, $keyname);
+       sel.matchdepid(dep, flags, keyname)
+       sel.matchdepid(dep, flags, keyname)
+
+Do a matchdepid operation and combine the result with the current selection.
+
        <stringification>
        my $str = $sel->str;
        str = str(sel)
index cc7707a..6014720 100644 (file)
@@ -606,6 +606,16 @@ packages, _idqinst_ for the latter one. This information can be stored in
 the meta section of the repositories to speed up the next time the
 repository is loaded and addfileprovides is called
 
+       void pool_set_whatprovides(pool, Id id, Id offset);
+
+Manually set an entry in the whatprovides index. You'll never do this for
+package dependencies, as those entries are created by calling the
+pool_createwhatprovides() function. But this function is useful for
+namespace provides if you do not want to use a namespace callback to
+lazily set the provides. The offset argument is a offset in the
+whatprovides array, thus you can use ``1'' as a false value and ``2''
+as true value.
+
        void pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr);
 
 Clear the cache of the providers for namespace dependencies matching
index 45debb1..9e90670 100755 (executable)
@@ -566,6 +566,7 @@ die("unknown command '$cmd'\n") unless defined $cmdactionmap{$cmd};
 
 $pool->addfileprovides();
 $pool->createwhatprovides();
+$pool->set_namespaceproviders($solv::NAMESPACE_LANGUAGE, $pool->Dep('de'), 1);
 
 my @jobs;
 for my $arg (@ARGV) {
index 2a5cd7f..7828379 100644 (file)
@@ -14,6 +14,9 @@
 #ifdef ENABLE_APPDATA
 #include "repo_appdata.h"
 #endif
+#ifdef SUSE
+#include "repo_autopattern.h"
+#endif
 
 #include "repoinfo.h"
 #include "repoinfo_cache.h"
@@ -195,6 +198,9 @@ repomd_load(struct repoinfo *cinfo, Pool **sigpoolp)
       fclose(fp);
     }
 #endif
+#ifdef SUSE
+  repo_add_autopattern(repo, 0);
+#endif
   data = repo_add_repodata(repo, 0);
   repodata_extend_block(data, repo->start, repo->end - repo->start);
   repomd_add_ext(repo, data, "deltainfo", "DL");
index c22c736..af933af 100644 (file)
@@ -12,6 +12,9 @@
 #ifdef ENABLE_APPDATA
 #include "repo_appdata.h"
 #endif
+#ifdef SUSE
+#include "repo_autopattern.h"
+#endif
 
 #include "repoinfo.h"
 #include "repoinfo_cache.h"
@@ -263,6 +266,9 @@ susetags_load(struct repoinfo *cinfo, Pool **sigpoolp)
     }
 #endif
   repo_internalize(repo);
+#ifdef SUSE
+  repo_add_autopattern(repo, 0);
+#endif
   data = repo_add_repodata(repo, 0);
   repodata_extend_block(data, repo->start, repo->end - repo->start);
   susetags_add_ext(repo, data);
index 5ee4297..bc9d87f 100644 (file)
@@ -533,14 +533,12 @@ main(int argc, char **argv)
        flags |= SELECTION_WITH_SOURCE;
       if (argv[i][0] == '/')
        flags |= SELECTION_FILELIST | (mode == MODE_ERASE ? SELECTION_INSTALLED_ONLY : 0);
+      if (keyname && keyname_depstr)
+       flags |= SELECTION_MATCH_DEPSTR;
       if (!keyname)
         rflags = selection_make(pool, &job2, argv[i], flags);
       else
-       {
-         if (keyname_depstr)
-           flags |= SELECTION_MATCH_DEPSTR;
-          rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
-       }
+        rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
       if (repofilter.count)
        selection_filter(pool, &job2, &repofilter);
       if (archfilter.count)
index ceaf351..ee226e7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012, Novell Inc.
+ * Copyright (c) 2007-2018, SUSE Inc.
  *
  * This program is licensed under the BSD license, read LICENSE.BSD
  * for further information
 #endif
 #include <rpm/rpmdb.h>
 
-#ifndef DB_CREATE
-# if defined(SUSE) || defined(HAVE_RPM_DB_H)
-#  include <rpm/db.h>
-# else
-#  include <db.h>
-# endif
-#endif
-
 #endif
 
 #include "pool.h"
 #define        TAG_SIGBASE             256
 #define TAG_SIGMD5             (TAG_SIGBASE + 5)
 #define TAG_SHA1HEADER         (TAG_SIGBASE + 13)
+#define TAG_SHA256HEADER       (TAG_SIGBASE + 17)
 
 #define SIGTAG_SIZE            1000
 #define SIGTAG_PGP             1002    /* RSA signature */
 #define DEP_PRE_IN             ((1 << 6) | (1 << 9) | (1 << 10))
 #define DEP_PRE_UN             ((1 << 6) | (1 << 11) | (1 << 12))
 
-#define FILEFLAG_GHOST         (1 <<  6)
+#define FILEFLAG_GHOST         (1 << 6)
 
 
-#ifdef RPM5
-# define RPM_INDEX_SIZE 4      /* just the rpmdbid */
-#else
-# define RPM_INDEX_SIZE 8      /* rpmdbid + array index */
-#endif
-
 /* some limits to guard against corrupt rpms */
 /* dsize limits taken from rpm's lib/header.c */
 #define MAX_SIG_CNT            0x10000
 #define MAX_HDR_CNT            0x10000
 #define MAX_HDR_DSIZE          0x10000000
 
+
+#ifndef ENABLE_RPMPKG_LIBRPM
+
 typedef struct rpmhead {
   int cnt;
   unsigned int dcnt;
@@ -204,10 +194,11 @@ headexists(RpmHead *h, int tag)
   return headfindtag(h, tag) ? 1 : 0;
 }
 
-static unsigned int *
+static uint32_t *
 headint32array(RpmHead *h, int tag, int *cnt)
 {
-  unsigned int i, o, *r;
+  uint32_t *r;
+  unsigned int i, o;
   unsigned char *d = headfindtag(h, tag);
 
   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 4)
@@ -217,7 +208,7 @@ headint32array(RpmHead *h, int tag, int *cnt)
   if (o > h->dcnt || i > h->dcnt || o + 4 * i > h->dcnt)
     return 0;
   d = h->dp + o;
-  r = solv_calloc(i ? i : 1, sizeof(unsigned int));
+  r = solv_calloc(i ? i : 1, sizeof(uint32_t));
   if (cnt)
     *cnt = i;
   for (o = 0; o < i; o++, d += 4)
@@ -226,7 +217,7 @@ headint32array(RpmHead *h, int tag, int *cnt)
 }
 
 /* returns the first entry of an integer array */
-static unsigned int
+static uint32_t
 headint32(RpmHead *h, int tag)
 {
   unsigned int i, o;
@@ -242,11 +233,11 @@ headint32(RpmHead *h, int tag)
   return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
 }
 
-static unsigned long long *
+static uint64_t *
 headint64array(RpmHead *h, int tag, int *cnt)
 {
+  uint64_t *r;
   unsigned int i, o;
-  unsigned long long *r;
   unsigned char *d = headfindtag(h, tag);
 
   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 5)
@@ -256,23 +247,25 @@ headint64array(RpmHead *h, int tag, int *cnt)
   if (o > h->dcnt || i > h->dcnt || o + 8 * i > h->dcnt)
     return 0;
   d = h->dp + o;
-  r = solv_calloc(i ? i : 1, sizeof(unsigned long long));
+  r = solv_calloc(i ? i : 1, sizeof(uint64_t));
   if (cnt)
     *cnt = i;
   for (o = 0; o < i; o++, d += 8)
     {
-      unsigned int x = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
-      r[o] = (unsigned long long)x << 32 | (unsigned int)(d[4] << 24 | d[5] << 16 | d[6] << 8 | d[7]);
+      uint32_t x = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
+      r[o] = (uint64_t)x << 32 | (uint32_t)(d[4] << 24 | d[5] << 16 | d[6] << 8 | d[7]);
     }
   return r;
 }
 
 /* returns the first entry of an 64bit integer array */
-static unsigned long long
+static uint64_t
 headint64(RpmHead *h, int tag)
 {
+  uint32_t x;
   unsigned int i, o;
   unsigned char *d = headfindtag(h, tag);
+
   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 5)
     return 0;
   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
@@ -280,14 +273,15 @@ headint64(RpmHead *h, int tag)
   if (i == 0 || o > h->dcnt || i > h->dcnt || o + 8 * i > h->dcnt)
     return 0;
   d = h->dp + o;
-  i = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
-  return (unsigned long long)i << 32 | (unsigned int)(d[4] << 24 | d[5] << 16 | d[6] << 8 | d[7]);
+  x = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
+  return (uint64_t)x << 32 | (uint32_t)(d[4] << 24 | d[5] << 16 | d[6] << 8 | d[7]);
 }
 
-static unsigned int *
+static uint16_t *
 headint16array(RpmHead *h, int tag, int *cnt)
 {
-  unsigned int i, o, *r;
+  uint16_t *r;
+  unsigned int i, o;
   unsigned char *d = headfindtag(h, tag);
 
   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 3)
@@ -297,7 +291,7 @@ headint16array(RpmHead *h, int tag, int *cnt)
   if (o > h->dcnt || i > h->dcnt || o + 2 * i > h->dcnt)
     return 0;
   d = h->dp + o;
-  r = solv_calloc(i ? i : 1, sizeof(unsigned int));
+  r = solv_calloc(i ? i : 1, sizeof(uint16_t));
   if (cnt)
     *cnt = i;
   for (o = 0; o < i; o++, d += 2)
@@ -378,6 +372,97 @@ headissourceheuristic(RpmHead *h)
   return i == 1 && o < h->dcnt && !h->dp[o] ? 1 : 0;
 }
 
+static inline void
+headfree(RpmHead *h)
+{
+  solv_free(h);
+}
+
+#else
+
+typedef struct headerToken_s RpmHead;
+
+static int
+headexists(RpmHead *h, int tag)
+{
+  return headerIsEntry(h, tag);
+}
+
+static void *headget(RpmHead *h, int tag, int *cnt, int alloc)
+{
+  struct rpmtd_s td;
+  if (!headerGet(h, tag, &td, alloc ? HEADERGET_ALLOC : HEADERGET_MINMEM))
+    return 0;
+  if (cnt)
+    *cnt = td.count;
+  return td.data;
+}
+
+static uint32_t *
+headint32array(RpmHead *h, int tag, int *cnt)
+{
+  return headget(h, tag, cnt, 1);
+}
+
+static uint32_t
+headint32(RpmHead *h, int tag)
+{
+  uint32_t *arr = headget(h, tag, 0, 0);
+  return arr ? arr[0] : 0;
+}
+
+static uint64_t *
+headint64array(RpmHead *h, int tag, int *cnt)
+{
+  return headget(h, tag, cnt, 1);
+}
+
+/* returns the first entry of an 64bit integer array */
+static uint64_t
+headint64(RpmHead *h, int tag)
+{
+  uint64_t *arr = headget(h, tag, 0, 0);
+  return arr ? arr[0] : 0;
+}
+
+static uint16_t *
+headint16array(RpmHead *h, int tag, int *cnt)
+{
+  return headget(h, tag, cnt, 1);
+}
+
+static char *
+headstring(RpmHead *h, int tag)
+{
+  return headget(h, tag, 0, 0);
+}
+
+static char **
+headstringarray(RpmHead *h, int tag, int *cnt)
+{
+  return headget(h, tag, cnt, 1);
+}
+
+static unsigned char *
+headbinary(RpmHead *h, int tag, unsigned int *sizep)
+{
+  return headget(h, tag, (int *)sizep, 0);
+}
+
+static int
+headissourceheuristic(RpmHead *h)
+{
+  return headerIsSource(h);
+}
+
+static inline void
+headfree(RpmHead *h)
+{
+  headerFree(h);
+}
+
+#endif
+
 static char *headtoevr(RpmHead *h)
 {
   unsigned int epoch;
@@ -451,7 +536,7 @@ static unsigned int
 makedeps(Pool *pool, Repo *repo, RpmHead *rpmhead, int tagn, int tagv, int tagf, int flags, Queue *ignq)
 {
   char **n, **v;
-  unsigned int *f;
+  uint32_t *f;
   int i, cc, nc, vc, fc;
   int haspre, premask, has_ign;
   unsigned int olddeps;
@@ -656,12 +741,14 @@ repodata_str2dir_rooted(Repodata *data, char *str, int create)
 }
 
 static void
-adddudata(Repodata *data, Id handle, RpmHead *rpmhead, char **dn, unsigned int *di, int fc, int dc)
+adddudata(Repodata *data, Id handle, RpmHead *rpmhead, char **dn, uint32_t *di, int fc, int dc)
 {
   Id did;
   int i, fszc;
-  unsigned int *fkb, *fn, *fsz, *fm, *fino;
-  unsigned long long *fsz64;
+  unsigned int *fkb, *fn;
+  uint64_t *fsz64;
+  uint32_t *fsz, *fino;
+  uint16_t *fm;
   unsigned int inotest[256], inotestok;
 
   if (!fc)
@@ -831,11 +918,11 @@ addfilelist(Repodata *data, Id handle, RpmHead *rpmhead, int flags)
 {
   char **bn;
   char **dn;
-  unsigned int *di;
+  uint32_t *di;
   int bnc, dnc, dic;
   int i;
   Id did;
-  unsigned int lastdii = -1;
+  uint32_t lastdii = -1;
   int lastfiltered = 0;
 
   if (!data)
@@ -904,7 +991,7 @@ addchangelog(Repodata *data, Id handle, RpmHead *rpmhead)
 {
   char **cn;
   char **cx;
-  unsigned int *ct;
+  uint32_t *ct;
   int i, cnc, cxc, ctc;
   Queue hq;
 
@@ -1121,27 +1208,6 @@ getu32(const unsigned char *dp)
   return dp[0] << 24 | dp[1] << 16 | dp[2] << 8 | dp[3];
 }
 
-
-/******************************************************************/
-/*  Rpm Database stuff
- */
-
-struct rpmdbstate {
-  Pool *pool;
-  char *rootdir;
-
-  RpmHead *rpmhead;    /* header storage space */
-  int rpmheadsize;
-
-#ifdef ENABLE_RPMDB
-  int dbopened;
-  DB_ENV *dbenv;       /* database environment */
-  DB *db;              /* packages database */
-  int byteswapped;     /* endianess of packages database */
-  int is_ostree;       /* read-only db that lives in /usr/share/rpm */
-#endif
-};
-
 #ifdef ENABLE_RPMDB
 
 struct rpmdbentry {
@@ -1152,160 +1218,103 @@ struct rpmdbentry {
 #define ENTRIES_BLOCK 255
 #define NAMEDATA_BLOCK 1023
 
-
-static inline Id
-db2rpmdbid(unsigned char *db, int byteswapped)
-{
-#ifdef RPM5
-  return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
-#else
-# if defined(WORDS_BIGENDIAN)
-  if (!byteswapped)
+# ifdef ENABLE_RPMDB_LIBRPM
+#  include "repo_rpmdb_librpm.h"
 # else
-  if (byteswapped)
+#  include "repo_rpmdb_bdb.h"
 # endif
-    return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
-  else
-    return db[3] << 24 | db[2] << 16 | db[1] << 8 | db[0];
-#endif
-}
 
-static inline void
-rpmdbid2db(unsigned char *db, Id id, int byteswapped)
-{
-#ifdef RPM5
-  db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
 #else
-# if defined(WORDS_BIGENDIAN)
-  if (!byteswapped)
-# else
-  if (byteswapped)
-# endif
-    db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
-  else
-    db[3] = id >> 24, db[2] = id >> 16, db[1] = id >> 8, db[0] = id;
-#endif
-}
 
-#if defined(FEDORA) || defined(MAGEIA)
-int
-serialize_dbenv_ops(struct rpmdbstate *state)
-{
-  char *lpath;
-  mode_t oldmask;
-  int fd;
-  struct flock fl;
-
-  lpath = solv_dupjoin(state->rootdir, "/var/lib/rpm/.dbenv.lock", 0);
-  oldmask = umask(022);
-  fd = open(lpath, (O_RDWR|O_CREAT), 0644);
-  free(lpath);
-  umask(oldmask);
-  if (fd < 0)
-    return -1;
-  memset(&fl, 0, sizeof(fl));
-  fl.l_type = F_WRLCK;
-  fl.l_whence = SEEK_SET;
-  for (;;)
-    {
-      if (fcntl(fd, F_SETLKW, &fl) != -1)
-       return fd;
-      if (errno != EINTR)
-       break;
-    }
-  close(fd);
-  return -1;
-}
+/* dummy state just to store pool/rootdir and header data */
+struct rpmdbstate {
+  Pool *pool;
+  char *rootdir;
+
+  RpmHead *rpmhead;    /* header storage space */
+  int rpmheadsize;
+};
+
 #endif
 
-/* should look in /usr/lib/rpm/macros instead, but we want speed... */
+
+#ifndef ENABLE_RPMPKG_LIBRPM
+
 static int
-opendbenv(struct rpmdbstate *state)
+headfromfp(struct rpmdbstate *state, const char *name, FILE *fp, unsigned char *lead, unsigned int cnt, unsigned int dsize, unsigned int pad, Chksum *chk1, Chksum *chk2)
 {
-  const char *rootdir = state->rootdir;
-  char *dbpath;
-  DB_ENV *dbenv = 0;
-  int r;
-
-  if (db_env_create(&dbenv, 0))
-    return pool_error(state->pool, 0, "db_env_create: %s", strerror(errno));
-#if (defined(FEDORA) || defined(MAGEIA)) && (DB_VERSION_MAJOR >= 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 5))
-  dbenv->set_thread_count(dbenv, 8);
-#endif
-  dbpath = solv_dupjoin(rootdir, "/var/lib/rpm", 0);
-  if (access(dbpath, W_OK) == -1)
-    {
-      free(dbpath);
-      dbpath = solv_dupjoin(rootdir, "/usr/share/rpm/Packages", 0);
-      if (access(dbpath, R_OK) == 0)
-       state->is_ostree = 1;
-      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
+  RpmHead *rpmhead;
+  unsigned int len = 16 * cnt + dsize + pad;
+  if (len + 1 > state->rpmheadsize)
     {
-#if defined(FEDORA) || defined(MAGEIA)
-      int serialize_fd = serialize_dbenv_ops(state);
-      r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_INIT_CDB|DB_INIT_MPOOL, 0644);
-      if (serialize_fd >= 0)
-       close(serialize_fd);
-#else
-      r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
-#endif
+      state->rpmheadsize = len + 128;
+      state->rpmhead = solv_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
     }
-  if (r)
+  rpmhead = state->rpmhead;
+  if (fread(rpmhead->data, len, 1, fp) != 1)
     {
-      pool_error(state->pool, 0, "dbenv->open: %s", strerror(errno));
-      free(dbpath);
-      dbenv->close(dbenv, 0);
-      return 0;
+      fclose(fp);
+      return pool_error(state->pool, 0, "%s: unexpected EOF", name);
     }
-  free(dbpath);
-  state->dbenv = dbenv;
+  if (chk1)
+    solv_chksum_add(chk1, rpmhead->data, len);
+  if (chk2)
+    solv_chksum_add(chk2, rpmhead->data, len);
+  rpmhead->data[len] = 0;
+  rpmhead->cnt = cnt;
+  rpmhead->dcnt = dsize;
+  rpmhead->dp = rpmhead->data + cnt * 16;
   return 1;
 }
 
+#if defined(ENABLE_RPMDB_BYRPMHEADER)
 static void
-closedbenv(struct rpmdbstate *state)
+headfromblob(struct rpmdbstate *state, const unsigned char *blob, unsigned int cnt, unsigned int dsize)
 {
-#if defined(FEDORA) || defined(MAGEIA)
-  uint32_t eflags = 0;
-#endif
-
-  if (!state->dbenv)
-    return;
-#if defined(FEDORA) || defined(MAGEIA)
-  (void)state->dbenv->get_open_flags(state->dbenv, &eflags);
-  if (!(eflags & DB_PRIVATE))
+  RpmHead *rpmhead;
+  unsigned int len = 16 * cnt + dsize;
+  if (len + 1 > state->rpmheadsize)
     {
-      int serialize_fd = serialize_dbenv_ops(state);
-      state->dbenv->close(state->dbenv, 0);
-      if (serialize_fd >= 0)
-       close(serialize_fd);
+      state->rpmheadsize = len + 128;
+      state->rpmhead = solv_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
     }
-  else
-    state->dbenv->close(state->dbenv, 0);
-#else
-  state->dbenv->close(state->dbenv, 0);
-#endif
-  state->dbenv = 0;
+  rpmhead = state->rpmhead;
+  memcpy(rpmhead->data, blob, len);
+  rpmhead->data[len] = 0;
+  rpmhead->cnt = cnt;
+  rpmhead->dcnt = dsize;
+  rpmhead->dp = rpmhead->data + cnt * 16;
 }
+#endif
+
+#else
 
 static int
-stat_database(struct rpmdbstate *state, char *dbname, struct stat *statbuf, int seterror)
+headfromfp(struct rpmdbstate *state, const char *name, FILE *fp, unsigned char *lead, unsigned int cnt, unsigned int dsize, unsigned int pad, Chksum *chk1, Chksum *chk2)
 {
-  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;
+  unsigned int len = 16 * cnt + dsize + pad;
+  char *buf = solv_malloc(8 + len);
+  Header h;
+  memcpy(buf, lead + 8, 8);
+  if (fread(buf + 8, len, 1, fp) != 1)
+    {
+      solv_free(buf);
+      return pool_error(state->pool, 0, "%s: unexpected EOF", name);
+    }
+  if (chk1)
+    solv_chksum_add(chk1, buf + 8, len);
+  if (chk2)
+    solv_chksum_add(chk2, buf + 8, len);
+  h = headerImport(buf, 8 + len - pad, HEADERIMPORT_FAST);
+  if (!h)
+    {
+      solv_free(buf);
+      return pool_error(state->pool, 0, "%s: headerImport error", name);
+    }
+  if (state->rpmhead)
+    headfree(state->rpmhead);
+  state->rpmhead = h;
+  return 1;
 }
 
 #endif
@@ -1314,17 +1323,15 @@ static void
 freestate(struct rpmdbstate *state)
 {
   /* close down */
-  if (!state)
-    return;
 #ifdef ENABLE_RPMDB
-  if (state->db)
-    state->db->close(state->db, 0);
-  if (state->dbenv)
+  if (state->pkgdbopened)
+    closepkgdb(state);
+  if (state->dbenvopened)
     closedbenv(state);
 #endif
   if (state->rootdir)
     solv_free(state->rootdir);
-  solv_free(state->rpmhead);
+  headfree(state->rpmhead);
 }
 
 void *
@@ -1341,255 +1348,14 @@ rpm_state_create(Pool *pool, const char *rootdir)
 void *
 rpm_state_free(void *state)
 {
-  freestate(state);
+  if (state)
+    freestate(state);
   return solv_free(state);
 }
 
 
 #ifdef ENABLE_RPMDB
 
-static int
-openpkgdb(struct rpmdbstate *state)
-{
-  if (state->dbopened)
-    return state->dbopened > 0 ? 1 : 0;
-  state->dbopened = -1;
-  if (!state->dbenv && !opendbenv(state))
-    return 0;
-  if (db_create(&state->db, state->dbenv, 0))
-    {
-      pool_error(state->pool, 0, "db_create: %s", strerror(errno));
-      state->db = 0;
-      closedbenv(state);
-      return 0;
-    }
-  if (state->db->open(state->db, 0, "Packages", 0, DB_UNKNOWN, DB_RDONLY, 0664))
-    {
-      pool_error(state->pool, 0, "db->open Packages: %s", strerror(errno));
-      state->db->close(state->db, 0);
-      state->db = 0;
-      closedbenv(state);
-      return 0;
-    }
-  if (state->db->get_byteswapped(state->db, &state->byteswapped))
-    {
-      pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
-      state->db->close(state->db, 0);
-      state->db = 0;
-      closedbenv(state);
-      return 0;
-    }
-  state->dbopened = 1;
-  return 1;
-}
-
-/* get the rpmdbids of all installed packages from the Name index database.
- * This is much faster then querying the big Packages database */
-static struct rpmdbentry *
-getinstalledrpmdbids(struct rpmdbstate *state, const char *index, const char *match, int *nentriesp, char **namedatap)
-{
-  DB_ENV *dbenv = 0;
-  DB *db = 0;
-  DBC *dbc = 0;
-  int byteswapped;
-  DBT dbkey;
-  DBT dbdata;
-  unsigned char *dp;
-  int dl;
-  Id nameoff;
-
-  char *namedata = 0;
-  int namedatal = 0;
-  struct rpmdbentry *entries = 0;
-  int nentries = 0;
-
-  *nentriesp = 0;
-  if (namedatap)
-    *namedatap = 0;
-
-  if (!state->dbenv && !opendbenv(state))
-    return 0;
-  dbenv = state->dbenv;
-  if (db_create(&db, dbenv, 0))
-    {
-      pool_error(state->pool, 0, "db_create: %s", strerror(errno));
-      return 0;
-    }
-  if (db->open(db, 0, index, 0, DB_UNKNOWN, DB_RDONLY, 0664))
-    {
-      pool_error(state->pool, 0, "db->open %s: %s", index, strerror(errno));
-      db->close(db, 0);
-      return 0;
-    }
-  if (db->get_byteswapped(db, &byteswapped))
-    {
-      pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
-      db->close(db, 0);
-      return 0;
-    }
-  if (db->cursor(db, NULL, &dbc, 0))
-    {
-      pool_error(state->pool, 0, "db->cursor: %s", strerror(errno));
-      db->close(db, 0);
-      return 0;
-    }
-  memset(&dbkey, 0, sizeof(dbkey));
-  memset(&dbdata, 0, sizeof(dbdata));
-  if (match)
-    {
-      dbkey.data = (void *)match;
-      dbkey.size = strlen(match);
-    }
-  while (dbc->c_get(dbc, &dbkey, &dbdata, match ? DB_SET : DB_NEXT) == 0)
-    {
-      if (!match && dbkey.size == 10 && !memcmp(dbkey.data, "gpg-pubkey", 10))
-       continue;
-      dl = dbdata.size;
-      dp = dbdata.data;
-      nameoff = namedatal;
-      if (namedatap)
-       {
-         namedata = solv_extend(namedata, namedatal, dbkey.size + 1, 1, NAMEDATA_BLOCK);
-         memcpy(namedata + namedatal, dbkey.data, dbkey.size);
-         namedata[namedatal + dbkey.size] = 0;
-         namedatal += dbkey.size + 1;
-       }
-      while(dl >= RPM_INDEX_SIZE)
-       {
-         entries = solv_extend(entries, nentries, 1, sizeof(*entries), ENTRIES_BLOCK);
-         entries[nentries].rpmdbid = db2rpmdbid(dp, byteswapped);
-         entries[nentries].nameoff = nameoff;
-         nentries++;
-         dp += RPM_INDEX_SIZE;
-         dl -= RPM_INDEX_SIZE;
-       }
-      if (match)
-       break;
-    }
-  dbc->c_close(dbc);
-  db->close(db, 0);
-  /* make sure that enteries is != 0 if there was no error */
-  if (!entries)
-    entries = solv_extend(entries, 1, 1, sizeof(*entries), ENTRIES_BLOCK);
-  *nentriesp = nentries;
-  if (namedatap)
-    *namedatap = namedata;
-  return entries;
-}
-
-/* common code, return dbid on success, -1 on error */
-static int
-getrpm_dbdata(struct rpmdbstate *state, DBT *dbdata, int dbid)
-{
-  unsigned int dsize, cnt, l;
-  RpmHead *rpmhead;
-
-  if (dbdata->size < 8)
-    return pool_error(state->pool, -1, "corrupt rpm database (size)");
-  cnt = getu32((const unsigned char *)dbdata->data);
-  dsize = getu32((const unsigned char *)dbdata->data + 4);
-  if (cnt >= MAX_HDR_CNT || dsize >= MAX_HDR_DSIZE)
-    return pool_error(state->pool, -1, "corrupt rpm database (cnt/dcnt)");
-  l = cnt * 16 + dsize;
-  if (8 + l > dbdata->size)
-    return pool_error(state->pool, -1, "corrupt rpm database (data size)");
-  if (l + 1 > state->rpmheadsize)
-    {
-      state->rpmheadsize = l + 128;
-      state->rpmhead = solv_realloc(state->rpmhead, sizeof(*rpmhead) + state->rpmheadsize);
-    }
-  rpmhead = state->rpmhead;
-  rpmhead->cnt = cnt;
-  rpmhead->dcnt = dsize;
-  memcpy(rpmhead->data, (unsigned char *)dbdata->data + 8, l);
-  rpmhead->data[l] = 0;
-  rpmhead->dp = rpmhead->data + cnt * 16;
-  return dbid;
-}
-
-/* retrive header by rpmdbid, returns 0 if not found, -1 on error */
-static int
-getrpm_dbid(struct rpmdbstate *state, Id dbid)
-{
-  unsigned char buf[4];
-  DBT dbkey;
-  DBT dbdata;
-
-  if (dbid <= 0)
-    return pool_error(state->pool, -1, "illegal rpmdbid %d", dbid);
-  if (state->dbopened != 1 && !openpkgdb(state))
-    return -1;
-  rpmdbid2db(buf, dbid, state->byteswapped);
-  memset(&dbkey, 0, sizeof(dbkey));
-  memset(&dbdata, 0, sizeof(dbdata));
-  dbkey.data = buf;
-  dbkey.size = 4;
-  dbdata.data = 0;
-  dbdata.size = 0;
-  if (state->db->get(state->db, NULL, &dbkey, &dbdata, 0))
-    return 0;
-  return getrpm_dbdata(state, &dbdata, dbid);
-}
-
-/* retrive header by berkeleydb cursor, returns 0 on EOF, -1 on error */
-static Id
-getrpm_cursor(struct rpmdbstate *state, DBC *dbc)
-{
-  DBT dbkey;
-  DBT dbdata;
-  Id dbid;
-
-  memset(&dbkey, 0, sizeof(dbkey));
-  memset(&dbdata, 0, sizeof(dbdata));
-  while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
-    {
-      if (dbkey.size != 4)
-       return pool_error(state->pool, -1, "corrupt Packages database (key size)");
-      dbid = db2rpmdbid(dbkey.data, state->byteswapped);
-      if (dbid)                /* ignore join key */
-        return getrpm_dbdata(state, &dbdata, dbid);
-    }
-  return 0;
-}
-
-static int
-count_headers(struct rpmdbstate *state)
-{
-  Pool *pool = state->pool;
-  struct stat statbuf;
-  DB *db = 0;
-  DBC *dbc = 0;
-  int count = 0;
-  DBT dbkey;
-  DBT dbdata;
-
-  if (stat_database(state, "Name", &statbuf, 0))
-    return 0;
-  memset(&dbkey, 0, sizeof(dbkey));
-  memset(&dbdata, 0, sizeof(dbdata));
-  if (db_create(&db, state->dbenv, 0))
-    {
-      pool_error(pool, 0, "db_create: %s", strerror(errno));
-      return 0;
-    }
-  if (db->open(db, 0, "Name", 0, DB_UNKNOWN, DB_RDONLY, 0664))
-    {
-      pool_error(pool, 0, "db->open Name: %s", strerror(errno));
-      db->close(db, 0);
-      return 0;
-    }
-  if (db->cursor(db, NULL, &dbc, 0))
-    {
-      db->close(db, 0);
-      pool_error(pool, 0, "db->cursor: %s", strerror(errno));
-      return 0;
-    }
-  while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
-    count += dbdata.size / RPM_INDEX_SIZE;
-  dbc->c_close(dbc);
-  db->close(db, 0);
-  return count;
-}
 
 /******************************************************************/
 
@@ -1903,7 +1669,6 @@ repo_add_rpmdb(Repo *repo, Repo *ref, int flags)
     {
       int solvstart = 0, solvend = 0;
       Id dbid;
-      DBC *dbc = 0;
 
       if (ref && (flags & RPMDB_EMPTY_REFREPO) != 0)
        repo_empty(ref, 1);     /* get it out of the way */
@@ -1914,18 +1679,18 @@ repo_add_rpmdb(Repo *repo, Repo *ref, int flags)
          freestate(&state);
          return -1;
        }
-      if (state.db->cursor(state.db, NULL, &dbc, 0))
+      if (pkgdb_cursor_open(&state))
        {
          freestate(&state);
-         return pool_error(pool, -1, "db->cursor failed");
+         return -1;
        }
       i = 0;
       s = 0;
-      while ((dbid = getrpm_cursor(&state, dbc)) != 0)
+      while ((dbid = pkgdb_cursor_getrpm(&state)) != 0)
        {
          if (dbid == -1)
            {
-             dbc->c_close(dbc);
+             pkgdb_cursor_close(&state);
              freestate(&state);
              return -1;
            }
@@ -1959,7 +1724,7 @@ repo_add_rpmdb(Repo *repo, Repo *ref, int flags)
                pool_debug(pool, SOLV_ERROR, "%%%% %d\n", done * 100 / count);
            }
        }
-      dbc->c_close(dbc);
+      pkgdb_cursor_close(&state);
       if (s)
        {
          /* oops, could not reuse. free it instead */
@@ -2141,16 +1906,15 @@ repo_add_rpmdb_reffp(Repo *repo, FILE *fp, int flags)
   return res;
 }
 
-#endif
+#endif /* ENABLE_RPMDB */
 
 Id
 repo_add_rpm(Repo *repo, const char *rpm, int flags)
 {
-  unsigned int sigdsize, sigcnt, l;
+  unsigned int sigdsize, sigcnt, sigpad, l;
   Pool *pool = repo->pool;
   Solvable *s;
-  RpmHead *rpmhead = 0;
-  int rpmheadsize = 0;
+  struct rpmdbstate state;
   char *payloadformat;
   FILE *fp;
   unsigned char lead[4096];
@@ -2172,6 +1936,7 @@ repo_add_rpm(Repo *repo, const char *rpm, int flags)
   else if ((flags & RPM_ADD_WITH_SHA1SUM) != 0)
     chksumtype = REPOKEY_TYPE_SHA1;
 
+  /* open rpm */
   if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, rpm) : rpm, "r")) == 0)
     {
       pool_error(pool, -1, "%s: %s", rpm, strerror(errno));
@@ -2183,6 +1948,12 @@ repo_add_rpm(Repo *repo, const char *rpm, int flags)
       fclose(fp);
       return 0;
     }
+
+  /* setup state */
+  memset(&state, 0, sizeof(state));
+  state.pool = pool;
+
+  /* process lead */
   if (chksumtype)
     chksumh = solv_chksum_create(chksumtype);
   if ((flags & RPM_ADD_WITH_LEADSIGID) != 0)
@@ -2197,6 +1968,8 @@ repo_add_rpm(Repo *repo, const char *rpm, int flags)
     solv_chksum_add(chksumh, lead, 96 + 16);
   if (leadsigchksumh)
     solv_chksum_add(leadsigchksumh, lead, 96 + 16);
+
+  /* process signature header */
   if (lead[78] != 0 || lead[79] != 5)
     {
       pool_error(pool, -1, "%s: not a rpm v5 header", rpm);
@@ -2217,37 +1990,21 @@ repo_add_rpm(Repo *repo, const char *rpm, int flags)
       fclose(fp);
       return 0;
     }
-  sigdsize += sigcnt * 16;
-  sigdsize = (sigdsize + 7) & ~7;
-  headerstart = 96 + 16 + sigdsize;
+  sigpad = sigdsize & 7 ? 8 - (sigdsize & 7) : 0;
+  headerstart = 96 + 16 + sigcnt * 16 + sigdsize + sigpad;
   pkgidtype = leadsigidtype = hdridtype = 0;
   if ((flags & (RPM_ADD_WITH_PKGID | RPM_ADD_WITH_HDRID)) != 0)
     {
-      /* extract pkgid or hdrid from the signature header */
-      if (sigdsize + 1 > rpmheadsize)
+      if (!headfromfp(&state, rpm, fp, lead + 96, sigcnt, sigdsize, sigpad, chksumh, leadsigchksumh))
        {
-         rpmheadsize = sigdsize + 128;
-         rpmhead = solv_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
-       }
-      if (fread(rpmhead->data, sigdsize, 1, fp) != 1)
-       {
-         pool_error(pool, -1, "%s: unexpected EOF", rpm);
          fclose(fp);
          return 0;
        }
-      rpmhead->data[sigdsize] = 0;
-      if (chksumh)
-       solv_chksum_add(chksumh, rpmhead->data, sigdsize);
-      if (leadsigchksumh)
-       solv_chksum_add(leadsigchksumh, rpmhead->data, sigdsize);
-      rpmhead->cnt = sigcnt;
-      rpmhead->dcnt = sigdsize - sigcnt * 16;
-      rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
       if ((flags & RPM_ADD_WITH_PKGID) != 0)
        {
          unsigned char *chksum;
          unsigned int chksumsize;
-         chksum = headbinary(rpmhead, SIGTAG_MD5, &chksumsize);
+         chksum = headbinary(state.rpmhead, SIGTAG_MD5, &chksumsize);
          if (chksum && chksumsize == 16)
            {
              pkgidtype = REPOKEY_TYPE_MD5;
@@ -2256,7 +2013,7 @@ repo_add_rpm(Repo *repo, const char *rpm, int flags)
        }
       if ((flags & RPM_ADD_WITH_HDRID) != 0)
        {
-         const char *str = headstring(rpmhead, TAG_SHA1HEADER);
+         const char *str = headstring(state.rpmhead, TAG_SHA1HEADER);
          if (str && strlen(str) == 40)
            {
              if (solv_hex2bin(&str, hdrid, 20) == 20)
@@ -2272,9 +2029,10 @@ repo_add_rpm(Repo *repo, const char *rpm, int flags)
   else
     {
       /* just skip the signature header */
-      while (sigdsize)
+      unsigned int len = sigcnt * 16 + sigdsize + sigpad;
+      while (len)
        {
-         l = sigdsize > 4096 ? 4096 : sigdsize;
+         l = len > 4096 ? 4096 : len;
          if (fread(lead, l, 1, fp) != 1)
            {
              pool_error(pool, -1, "%s: unexpected EOF", rpm);
@@ -2285,7 +2043,7 @@ repo_add_rpm(Repo *repo, const char *rpm, int flags)
            solv_chksum_add(chksumh, lead, l);
          if (leadsigchksumh)
            solv_chksum_add(leadsigchksumh, lead, l);
-         sigdsize -= l;
+         len -= l;
        }
     }
   if (leadsigchksumh)
@@ -2293,6 +2051,8 @@ repo_add_rpm(Repo *repo, const char *rpm, int flags)
       leadsigchksumh = solv_chksum_free(leadsigchksumh, leadsigid);
       leadsigidtype = REPOKEY_TYPE_MD5;
     }
+
+  /* process main header */
   if (fread(lead, 16, 1, fp) != 1)
     {
       pool_error(pool, -1, "%s: unexpected EOF", rpm);
@@ -2315,42 +2075,30 @@ repo_add_rpm(Repo *repo, const char *rpm, int flags)
       fclose(fp);
       return 0;
     }
-  l = sigdsize + sigcnt * 16;
-  headerend = headerstart + 16 + l;
-  if (l + 1 > rpmheadsize)
-    {
-      rpmheadsize = l + 128;
-      rpmhead = solv_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
-    }
-  if (fread(rpmhead->data, l, 1, fp) != 1)
+  headerend = headerstart + 16 + sigdsize + sigcnt * 16;
+
+  if (!headfromfp(&state, rpm, fp, lead, sigcnt, sigdsize, 0, chksumh, 0))
     {
-      pool_error(pool, -1, "%s: unexpected EOF", rpm);
       fclose(fp);
       return 0;
     }
-  rpmhead->data[l] = 0;
-  if (chksumh)
-    solv_chksum_add(chksumh, rpmhead->data, l);
-  rpmhead->cnt = sigcnt;
-  rpmhead->dcnt = sigdsize;
-  rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
-  if (headexists(rpmhead, TAG_PATCHESNAME))
+  if (headexists(state.rpmhead, TAG_PATCHESNAME))
     {
       /* this is a patch rpm, ignore */
       pool_error(pool, -1, "%s: is patch rpm", rpm);
       fclose(fp);
       solv_chksum_free(chksumh, 0);
-      solv_free(rpmhead);
+      headfree(state.rpmhead);
       return 0;
     }
-  payloadformat = headstring(rpmhead, TAG_PAYLOADFORMAT);
+  payloadformat = headstring(state.rpmhead, TAG_PAYLOADFORMAT);
   if (payloadformat && !strcmp(payloadformat, "drpm"))
     {
       /* this is a delta rpm */
       pool_error(pool, -1, "%s: is delta rpm", rpm);
       fclose(fp);
       solv_chksum_free(chksumh, 0);
-      solv_free(rpmhead);
+      headfree(state.rpmhead);
       return 0;
     }
   if (chksumh)
@@ -2358,11 +2106,11 @@ repo_add_rpm(Repo *repo, const char *rpm, int flags)
       solv_chksum_add(chksumh, lead, l);
   fclose(fp);
   s = pool_id2solvable(pool, repo_add_solvable(repo));
-  if (!rpmhead2solv(pool, repo, data, s, rpmhead, flags & ~(RPM_ADD_WITH_HDRID | RPM_ADD_WITH_PKGID)))
+  if (!rpmhead2solv(pool, repo, data, s, state.rpmhead, flags & ~(RPM_ADD_WITH_HDRID | RPM_ADD_WITH_PKGID)))
     {
       repo_free_solvable(repo, s - pool->solvables, 1);
       solv_chksum_free(chksumh, 0);
-      solv_free(rpmhead);
+      headfree(state.rpmhead);
       return 0;
     }
   if (!(flags & REPO_NO_LOCATION))
@@ -2381,7 +2129,7 @@ repo_add_rpm(Repo *repo, const char *rpm, int flags)
       repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, chksumtype, solv_chksum_get(chksumh, 0));
       chksumh = solv_chksum_free(chksumh, 0);
     }
-  solv_free(rpmhead);
+  headfree(state.rpmhead);
   if (!(flags & REPO_NO_INTERNALIZE))
     repodata_internalize(data);
   return s - pool->solvables;
@@ -2441,12 +2189,12 @@ rpm_iterate_filelist(void *rpmhandle, int flags, void (*cb)(void *, const char *
   char **dn;
   char **md = 0;
   char **lt = 0;
-  unsigned int *di, diidx;
-  unsigned int *co = 0;
-  unsigned int *ff = 0;
+  uint32_t *di, diidx;
+  uint32_t *co = 0;
+  uint32_t *ff = 0;
+  uint16_t *fm;
   unsigned int lastdir;
   int lastdirl;
-  unsigned int *fm;
   int cnt, dcnt, cnt2;
   int i, l1, l;
   char *space = 0;
@@ -2693,14 +2441,12 @@ rpm_byrpmdbid(void *rpmstate, Id rpmdbid)
   return r <= 0 ? 0 : state->rpmhead;
 }
 
-#endif
+#endif /* ENABLE_RPMDB */
 
 void *
 rpm_byfp(void *rpmstate, FILE *fp, const char *name)
 {
   struct rpmdbstate *state = rpmstate;
-  /* int headerstart, headerend; */
-  RpmHead *rpmhead;
   unsigned int sigdsize, sigcnt, l;
   unsigned char lead[4096];
 
@@ -2714,6 +2460,8 @@ rpm_byfp(void *rpmstate, FILE *fp, const char *name)
       pool_error(state->pool, 0, "%s: not a V5 header", name);
       return 0;
     }
+
+  /* skip signature header */
   if (getu32(lead + 96) != 0x8eade801)
     {
       pool_error(state->pool, 0, "%s: bad signature header", name);
@@ -2728,7 +2476,6 @@ rpm_byfp(void *rpmstate, FILE *fp, const char *name)
     }
   sigdsize += sigcnt * 16;
   sigdsize = (sigdsize + 7) & ~7;
-  /* headerstart = 96 + 16 + sigdsize; */
   while (sigdsize)
     {
       l = sigdsize > 4096 ? 4096 : sigdsize;
@@ -2739,6 +2486,7 @@ rpm_byfp(void *rpmstate, FILE *fp, const char *name)
        }
       sigdsize -= l;
     }
+
   if (fread(lead, 16, 1, fp) != 1)
     {
       pool_error(state->pool, 0, "%s: unexpected EOF", name);
@@ -2756,36 +2504,23 @@ rpm_byfp(void *rpmstate, FILE *fp, const char *name)
       pool_error(state->pool, 0, "%s: bad header", name);
       return 0;
     }
-  l = sigdsize + sigcnt * 16;
-  /* headerend = headerstart + 16 + l; */
-  if (l + 1 > state->rpmheadsize)
-    {
-      state->rpmheadsize = l + 128;
-      state->rpmhead = solv_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
-    }
-  rpmhead = state->rpmhead;
-  if (fread(rpmhead->data, l, 1, fp) != 1)
-    {
-      pool_error(state->pool, 0, "%s: unexpected EOF", name);
-      return 0;
-    }
-  rpmhead->data[l] = 0;
-  rpmhead->cnt = sigcnt;
-  rpmhead->dcnt = sigdsize;
-  rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
-  return rpmhead;
+  if (!headfromfp(state, name, fp, lead, sigcnt, sigdsize, 0, 0, 0))
+    return 0;
+  return state->rpmhead;
 }
 
-#ifdef ENABLE_RPMDB_BYRPMHEADER
+#if defined(ENABLE_RPMDB_BYRPMHEADER) || defined(ENABLE_RPMDB_LIBRPM)
 
 void *
 rpm_byrpmh(void *rpmstate, Header h)
 {
   struct rpmdbstate *state = rpmstate;
+#ifndef ENABLE_RPMPKG_LIBRPM
   const unsigned char *uh;
-  unsigned int sigdsize, sigcnt, l;
-  RpmHead *rpmhead;
+  unsigned int dsize, cnt;
 
+  if (!h)
+    return 0;
 #ifndef RPM5
   uh = headerUnload(h);
 #else
@@ -2793,25 +2528,24 @@ rpm_byrpmh(void *rpmstate, Header h)
 #endif
   if (!uh)
     return 0;
-  sigcnt = getu32(uh);
-  sigdsize = getu32(uh + 4);
-  if (sigcnt >= MAX_HDR_CNT || sigdsize >= MAX_HDR_DSIZE)
-    return 0;
-  l = sigdsize + sigcnt * 16;
-  if (l + 1 > state->rpmheadsize)
+  cnt = getu32(uh);
+  dsize = getu32(uh + 4);
+  if (cnt >= MAX_HDR_CNT || dsize >= MAX_HDR_DSIZE)
     {
-      state->rpmheadsize = l + 128;
-      state->rpmhead = solv_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
+      free((void *)uh);
+      return 0;
     }
-  rpmhead = state->rpmhead;
-  memcpy(rpmhead->data, uh + 8, l);
-  rpmhead->data[l] = 0;
+  headfromblob(state, uh + 8, cnt, dsize);
   free((void *)uh);
-  rpmhead->cnt = sigcnt;
-  rpmhead->dcnt = sigdsize;
-  rpmhead->dp = rpmhead->data + sigcnt * 16;
-  return rpmhead;
+#else
+  if (!h)
+    return 0;
+  if (state->rpmhead)
+    headfree(state->rpmhead);
+  state->rpmhead = headerLink(h);
+#endif
+  return state->rpmhead;
 }
 
-#endif
+#endif /* defined(ENABLE_RPMDB_BYRPMHEADER) || defined(ENABLE_RPMDB_LIBRPM) */
 
diff --git a/ext/repo_rpmdb_bdb.h b/ext/repo_rpmdb_bdb.h
new file mode 100644 (file)
index 0000000..c34ff70
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * Copyright (c) 2018 SUSE Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repo_rpmdb_bdb.h
+ *
+ * Use BerkeleyDB to access the rpm database
+ *
+ */
+
+
+#if !defined(DB_CREATE) && !defined(ENABLE_RPMDB_LIBRPM)
+# if defined(SUSE) || defined(HAVE_RPM_DB_H)
+#  include <rpm/db.h>
+# else
+#  include <db.h>
+# endif
+#endif
+
+#ifdef RPM5
+# define RPM_INDEX_SIZE 4      /* just the rpmdbid */
+#else
+# define RPM_INDEX_SIZE 8      /* rpmdbid + array index */
+#endif
+
+
+/******************************************************************/
+/*  Rpm Database stuff
+ */
+
+struct rpmdbstate {
+  Pool *pool;
+  char *rootdir;
+
+  RpmHead *rpmhead;    /* header storage space */
+  int rpmheadsize;
+
+  int dbenvopened;     /* database environment opened */
+  int pkgdbopened;     /* package database openend */
+  int is_ostree;       /* read-only db that lives in /usr/share/rpm */
+
+  DB_ENV *dbenv;       /* database environment */
+  DB *db;              /* packages database */
+  int byteswapped;     /* endianess of packages database */
+  DBC *dbc;            /* iterator over packages database */
+};
+
+
+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;
+}
+
+
+static inline Id
+db2rpmdbid(unsigned char *db, int byteswapped)
+{
+#ifdef RPM5
+  return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
+#else
+# if defined(WORDS_BIGENDIAN)
+  if (!byteswapped)
+# else
+  if (byteswapped)
+# endif
+    return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
+  else
+    return db[3] << 24 | db[2] << 16 | db[1] << 8 | db[0];
+#endif
+}
+
+static inline void
+rpmdbid2db(unsigned char *db, Id id, int byteswapped)
+{
+#ifdef RPM5
+  db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
+#else
+# if defined(WORDS_BIGENDIAN)
+  if (!byteswapped)
+# else
+  if (byteswapped)
+# endif
+    db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
+  else
+    db[3] = id >> 24, db[2] = id >> 16, db[1] = id >> 8, db[0] = id;
+#endif
+}
+
+#if defined(FEDORA) || defined(MAGEIA)
+static int
+serialize_dbenv_ops(struct rpmdbstate *state)
+{
+  char *lpath;
+  mode_t oldmask;
+  int fd;
+  struct flock fl;
+
+  lpath = solv_dupjoin(state->rootdir, "/var/lib/rpm/.dbenv.lock", 0);
+  oldmask = umask(022);
+  fd = open(lpath, (O_RDWR|O_CREAT), 0644);
+  free(lpath);
+  umask(oldmask);
+  if (fd < 0)
+    return -1;
+  memset(&fl, 0, sizeof(fl));
+  fl.l_type = F_WRLCK;
+  fl.l_whence = SEEK_SET;
+  for (;;)
+    {
+      if (fcntl(fd, F_SETLKW, &fl) != -1)
+       return fd;
+      if (errno != EINTR)
+       break;
+    }
+  close(fd);
+  return -1;
+}
+
+#endif
+
+/* should look in /usr/lib/rpm/macros instead, but we want speed... */
+static int
+opendbenv(struct rpmdbstate *state)
+{
+  const char *rootdir = state->rootdir;
+  char *dbpath;
+  DB_ENV *dbenv = 0;
+  int r;
+
+  if (db_env_create(&dbenv, 0))
+    return pool_error(state->pool, 0, "db_env_create: %s", strerror(errno));
+#if (defined(FEDORA) || defined(MAGEIA)) && (DB_VERSION_MAJOR >= 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 5))
+  dbenv->set_thread_count(dbenv, 8);
+#endif
+  dbpath = solv_dupjoin(rootdir, "/var/lib/rpm", 0);
+  if (access(dbpath, W_OK) == -1)
+    {
+      free(dbpath);
+      dbpath = solv_dupjoin(rootdir, "/usr/share/rpm/Packages", 0);
+      if (access(dbpath, R_OK) == 0)
+       state->is_ostree = 1;
+      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
+    {
+#if defined(FEDORA) || defined(MAGEIA)
+      int serialize_fd = serialize_dbenv_ops(state);
+      int eflags = DB_CREATE|DB_INIT_CDB|DB_INIT_MPOOL;
+      r = dbenv->open(dbenv, dbpath, eflags, 0644);
+      /* see rpm commit 2822ccbcdf3e898b960fafb23c4d571e26cef0a4 */
+      if (r == DB_VERSION_MISMATCH)
+       {
+         eflags |= DB_PRIVATE;
+         dbenv->errx(dbenv, "warning: DB_VERSION_MISMATCH, retrying with DB_PRIVATE");
+         r = dbenv->open(dbenv, dbpath, eflags, 0644);
+       }
+      if (serialize_fd >= 0)
+       close(serialize_fd);
+#else
+      r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
+#endif
+    }
+  if (r)
+    {
+      pool_error(state->pool, 0, "dbenv->open: %s", strerror(errno));
+      free(dbpath);
+      dbenv->close(dbenv, 0);
+      return 0;
+    }
+  free(dbpath);
+  state->dbenv = dbenv;
+  state->dbenvopened = 1;
+  return 1;
+}
+
+static void
+closedbenv(struct rpmdbstate *state)
+{
+#if defined(FEDORA) || defined(MAGEIA)
+  uint32_t eflags = 0;
+#endif
+
+  if (!state->dbenv)
+    return;
+#if defined(FEDORA) || defined(MAGEIA)
+  (void)state->dbenv->get_open_flags(state->dbenv, &eflags);
+  if (!(eflags & DB_PRIVATE))
+    {
+      int serialize_fd = serialize_dbenv_ops(state);
+      state->dbenv->close(state->dbenv, 0);
+      if (serialize_fd >= 0)
+       close(serialize_fd);
+    }
+  else
+    state->dbenv->close(state->dbenv, 0);
+#else
+  state->dbenv->close(state->dbenv, 0);
+#endif
+  state->dbenv = 0;
+  state->dbenvopened = 0;
+}
+
+static int
+openpkgdb(struct rpmdbstate *state)
+{
+  if (state->pkgdbopened)
+    return state->pkgdbopened > 0 ? 1 : 0;
+  state->pkgdbopened = -1;
+  if (state->dbenvopened != 1 && !opendbenv(state))
+    return 0;
+  if (db_create(&state->db, state->dbenv, 0))
+    {
+      pool_error(state->pool, 0, "db_create: %s", strerror(errno));
+      state->db = 0;
+      closedbenv(state);
+      return 0;
+    }
+  if (state->db->open(state->db, 0, "Packages", 0, DB_UNKNOWN, DB_RDONLY, 0664))
+    {
+      pool_error(state->pool, 0, "db->open Packages: %s", strerror(errno));
+      state->db->close(state->db, 0);
+      state->db = 0;
+      closedbenv(state);
+      return 0;
+    }
+  if (state->db->get_byteswapped(state->db, &state->byteswapped))
+    {
+      pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
+      state->db->close(state->db, 0);
+      state->db = 0;
+      closedbenv(state);
+      return 0;
+    }
+  state->pkgdbopened = 1;
+  return 1;
+}
+
+static void
+closepkgdb(struct rpmdbstate *state)
+{
+  if (!state->db)
+    return;
+  state->db->close(state->db, 0);
+  state->db = 0;
+  state->pkgdbopened = 0;
+}
+
+/* get the rpmdbids of all installed packages from the Name index database.
+ * This is much faster then querying the big Packages database */
+static struct rpmdbentry *
+getinstalledrpmdbids(struct rpmdbstate *state, const char *index, const char *match, int *nentriesp, char **namedatap)
+{
+  DB_ENV *dbenv = 0;
+  DB *db = 0;
+  DBC *dbc = 0;
+  int byteswapped;
+  DBT dbkey;
+  DBT dbdata;
+  unsigned char *dp;
+  int dl;
+  Id nameoff;
+
+  char *namedata = 0;
+  int namedatal = 0;
+  struct rpmdbentry *entries = 0;
+  int nentries = 0;
+
+  *nentriesp = 0;
+  if (namedatap)
+    *namedatap = 0;
+
+  if (state->dbenvopened != 1 && !opendbenv(state))
+    return 0;
+  dbenv = state->dbenv;
+  if (db_create(&db, dbenv, 0))
+    {
+      pool_error(state->pool, 0, "db_create: %s", strerror(errno));
+      return 0;
+    }
+  if (db->open(db, 0, index, 0, DB_UNKNOWN, DB_RDONLY, 0664))
+    {
+      pool_error(state->pool, 0, "db->open %s: %s", index, strerror(errno));
+      db->close(db, 0);
+      return 0;
+    }
+  if (db->get_byteswapped(db, &byteswapped))
+    {
+      pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
+      db->close(db, 0);
+      return 0;
+    }
+  if (db->cursor(db, NULL, &dbc, 0))
+    {
+      pool_error(state->pool, 0, "db->cursor: %s", strerror(errno));
+      db->close(db, 0);
+      return 0;
+    }
+  memset(&dbkey, 0, sizeof(dbkey));
+  memset(&dbdata, 0, sizeof(dbdata));
+  if (match)
+    {
+      dbkey.data = (void *)match;
+      dbkey.size = strlen(match);
+    }
+  while (dbc->c_get(dbc, &dbkey, &dbdata, match ? DB_SET : DB_NEXT) == 0)
+    {
+      if (!match && dbkey.size == 10 && !memcmp(dbkey.data, "gpg-pubkey", 10))
+       continue;
+      dl = dbdata.size;
+      dp = dbdata.data;
+      nameoff = namedatal;
+      if (namedatap)
+       {
+         namedata = solv_extend(namedata, namedatal, dbkey.size + 1, 1, NAMEDATA_BLOCK);
+         memcpy(namedata + namedatal, dbkey.data, dbkey.size);
+         namedata[namedatal + dbkey.size] = 0;
+         namedatal += dbkey.size + 1;
+       }
+      while(dl >= RPM_INDEX_SIZE)
+       {
+         entries = solv_extend(entries, nentries, 1, sizeof(*entries), ENTRIES_BLOCK);
+         entries[nentries].rpmdbid = db2rpmdbid(dp, byteswapped);
+         entries[nentries].nameoff = nameoff;
+         nentries++;
+         dp += RPM_INDEX_SIZE;
+         dl -= RPM_INDEX_SIZE;
+       }
+      if (match)
+       break;
+    }
+  dbc->c_close(dbc);
+  db->close(db, 0);
+  /* make sure that enteries is != 0 if there was no error */
+  if (!entries)
+    entries = solv_extend(entries, 1, 1, sizeof(*entries), ENTRIES_BLOCK);
+  *nentriesp = nentries;
+  if (namedatap)
+    *namedatap = namedata;
+  return entries;
+}
+
+/* common code, return dbid on success, -1 on error */
+static int
+getrpm_dbdata(struct rpmdbstate *state, DBT *dbdata, int dbid)
+{
+  unsigned int dsize, cnt, l;
+  RpmHead *rpmhead;
+
+  if (dbdata->size < 8)
+    return pool_error(state->pool, -1, "corrupt rpm database (size)");
+  cnt = getu32((const unsigned char *)dbdata->data);
+  dsize = getu32((const unsigned char *)dbdata->data + 4);
+  if (cnt >= MAX_HDR_CNT || dsize >= MAX_HDR_DSIZE)
+    return pool_error(state->pool, -1, "corrupt rpm database (cnt/dcnt)");
+  l = cnt * 16 + dsize;
+  if (8 + l > dbdata->size)
+    return pool_error(state->pool, -1, "corrupt rpm database (data size)");
+  if (l + 1 > state->rpmheadsize)
+    {
+      state->rpmheadsize = l + 128;
+      state->rpmhead = solv_realloc(state->rpmhead, sizeof(*rpmhead) + state->rpmheadsize);
+    }
+  rpmhead = state->rpmhead;
+  rpmhead->cnt = cnt;
+  rpmhead->dcnt = dsize;
+  memcpy(rpmhead->data, (unsigned char *)dbdata->data + 8, l);
+  rpmhead->data[l] = 0;
+  rpmhead->dp = rpmhead->data + cnt * 16;
+  return dbid;
+}
+
+/* retrive header by rpmdbid, returns 0 if not found, -1 on error */
+static int
+getrpm_dbid(struct rpmdbstate *state, Id dbid)
+{
+  unsigned char buf[4];
+  DBT dbkey;
+  DBT dbdata;
+
+  if (dbid <= 0)
+    return pool_error(state->pool, -1, "illegal rpmdbid %d", dbid);
+  if (state->pkgdbopened != 1 && !openpkgdb(state))
+    return -1;
+  rpmdbid2db(buf, dbid, state->byteswapped);
+  memset(&dbkey, 0, sizeof(dbkey));
+  memset(&dbdata, 0, sizeof(dbdata));
+  dbkey.data = buf;
+  dbkey.size = 4;
+  dbdata.data = 0;
+  dbdata.size = 0;
+  if (state->db->get(state->db, NULL, &dbkey, &dbdata, 0))
+    return 0;
+  return getrpm_dbdata(state, &dbdata, dbid);
+}
+
+static int
+count_headers(struct rpmdbstate *state)
+{
+  Pool *pool = state->pool;
+  struct stat statbuf;
+  DB *db = 0;
+  DBC *dbc = 0;
+  int count = 0;
+  DBT dbkey;
+  DBT dbdata;
+
+  if (stat_database(state, "Name", &statbuf, 0))
+    return 0;
+  memset(&dbkey, 0, sizeof(dbkey));
+  memset(&dbdata, 0, sizeof(dbdata));
+  if (db_create(&db, state->dbenv, 0))
+    {
+      pool_error(pool, 0, "db_create: %s", strerror(errno));
+      return 0;
+    }
+  if (db->open(db, 0, "Name", 0, DB_UNKNOWN, DB_RDONLY, 0664))
+    {
+      pool_error(pool, 0, "db->open Name: %s", strerror(errno));
+      db->close(db, 0);
+      return 0;
+    }
+  if (db->cursor(db, NULL, &dbc, 0))
+    {
+      db->close(db, 0);
+      pool_error(pool, 0, "db->cursor: %s", strerror(errno));
+      return 0;
+    }
+  while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
+    count += dbdata.size / RPM_INDEX_SIZE;
+  dbc->c_close(dbc);
+  db->close(db, 0);
+  return count;
+}
+
+static int
+pkgdb_cursor_open(struct rpmdbstate *state)
+{
+  if (state->db->cursor(state->db, NULL, &state->dbc, 0))
+    return pool_error(state->pool, -1, "db->cursor failed");
+  return 0;
+}
+
+static void
+pkgdb_cursor_close(struct rpmdbstate *state)
+{
+  state->dbc->c_close(state->dbc);
+  state->dbc = 0;
+}
+
+/* retrive header by berkeleydb cursor, returns 0 on EOF, -1 on error */
+static Id
+pkgdb_cursor_getrpm(struct rpmdbstate *state)
+{
+  DBT dbkey;
+  DBT dbdata;
+  Id dbid;
+
+  memset(&dbkey, 0, sizeof(dbkey));
+  memset(&dbdata, 0, sizeof(dbdata));
+  while (state->dbc->c_get(state->dbc, &dbkey, &dbdata, DB_NEXT) == 0)
+    {
+      if (dbkey.size != 4)
+       return pool_error(state->pool, -1, "corrupt Packages database (key size)");
+      dbid = db2rpmdbid(dbkey.data, state->byteswapped);
+      if (dbid)                /* ignore join key */
+        return getrpm_dbdata(state, &dbdata, dbid);
+    }
+  return 0;    /* no more entries */
+}
+
diff --git a/ext/repo_rpmdb_librpm.h b/ext/repo_rpmdb_librpm.h
new file mode 100644 (file)
index 0000000..802c5d1
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2018, SUSE Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * repo_rpmdb_librpm.h
+ *
+ * Use librpm to access the rpm database
+ *
+ */
+
+#include <rpm/rpmts.h>
+#include <rpm/rpmmacro.h>
+
+struct rpmdbstate {
+  Pool *pool;
+  char *rootdir;
+
+  RpmHead *rpmhead;    /* header storage space */
+  int rpmheadsize;
+
+  int dbenvopened;     /* database environment opened */
+  int pkgdbopened;     /* package database openend */
+  int is_ostree;       /* read-only db that lives in /usr/share/rpm */
+
+  rpmts ts;
+  rpmdbMatchIterator mi;       /* iterator over packages database */
+};
+
+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;
+}
+
+static int
+opendbenv(struct rpmdbstate *state)
+{
+  const char *rootdir = state->rootdir;
+  rpmts ts;
+  char *dbpath;
+  dbpath = solv_dupjoin("_dbpath ", rootdir, "/var/lib/rpm");
+  if (access(dbpath + 8, W_OK) == -1)
+    {
+      free(dbpath);
+      dbpath = solv_dupjoin(rootdir, "/usr/share/rpm/Packages", 0);
+      if (access(dbpath, R_OK) == 0)
+       state->is_ostree = 1;
+      free(dbpath);
+      dbpath = solv_dupjoin("_dbpath ", rootdir, state->is_ostree ? "/usr/share/rpm" : "/var/lib/rpm");
+    }
+  rpmDefineMacro(NULL, dbpath, 0);
+  solv_free(dbpath);
+  ts = rpmtsCreate();
+  if (!ts)
+    {
+      pool_error(state->pool, 0, "rpmtsCreate failed");
+      delMacro(NULL, "_dbpath");
+      return 0;
+    }
+  if (rpmtsOpenDB(ts, O_RDONLY))
+    {
+      pool_error(state->pool, 0, "rpmtsOpenDB failed: %s", strerror(errno));
+      rpmtsFree(ts);
+      delMacro(NULL, "_dbpath");
+      return 0;
+    }
+  delMacro(NULL, "_dbpath");
+  rpmtsSetVSFlags(ts, _RPMVSF_NODIGESTS | _RPMVSF_NOSIGNATURES | _RPMVSF_NOHEADER);
+  state->ts = ts;
+  state->dbenvopened = 1;
+  state->pkgdbopened = 1;
+  return 1;
+}
+
+static void
+closedbenv(struct rpmdbstate *state)
+{
+  if (state->ts)
+    rpmtsFree(state->ts);
+  state->ts = 0;
+  state->pkgdbopened = 0;
+  state->dbenvopened = 0;
+}
+
+static int
+openpkgdb(struct rpmdbstate *state)
+{
+  /* already done in opendbenv */
+  return 1;
+}
+
+static void
+closepkgdb(struct rpmdbstate *state)
+{
+}
+
+/* get the rpmdbids of all installed packages from the Name index database.
+ * This is much faster then querying the big Packages database */
+static struct rpmdbentry *
+getinstalledrpmdbids(struct rpmdbstate *state, const char *index, const char *match, int *nentriesp, char **namedatap)
+{
+  const void * key;
+  size_t keylen;
+  Id nameoff;
+
+  char *namedata = 0;
+  int namedatal = 0;
+  struct rpmdbentry *entries = 0;
+  int nentries = 0;
+
+  rpmdbIndexIterator ii;
+  int i;
+
+  if (state->dbenvopened != 1 && !opendbenv(state))
+    return 0;
+
+  ii = rpmdbIndexIteratorInit(rpmtsGetRdb(state->ts), RPMDBI_NAME);
+
+  *nentriesp = 0;
+  if (namedatap)
+    *namedatap = 0;
+
+  while (rpmdbIndexIteratorNext(ii, &key, &keylen) == 0)
+    {
+
+      if (keylen == 10 && !memcmp(key, "gpg-pubkey", 10))
+       continue;
+      nameoff = namedatal;
+      if (namedatap)
+       {
+         namedata = solv_extend(namedata, namedatal, keylen + 1, 1, NAMEDATA_BLOCK);
+         memcpy(namedata + namedatal, key, keylen);
+         namedata[namedatal + keylen] = 0;
+         namedatal += keylen + 1;
+       }
+      for (i = 0; i < rpmdbIndexIteratorNumPkgs(ii); i++)
+       {
+         entries = solv_extend(entries, nentries, 1, sizeof(*entries), ENTRIES_BLOCK);
+         entries[nentries].rpmdbid = rpmdbIndexIteratorPkgOffset(ii, i);
+         entries[nentries].nameoff = nameoff;
+         nentries++;
+       }
+    }
+  rpmdbIndexIteratorFree(ii);
+  /* make sure that enteries is != 0 if there was no error */
+  if (!entries)
+    entries = solv_extend(entries, 1, 1, sizeof(*entries), ENTRIES_BLOCK);
+  *nentriesp = nentries;
+  if (namedatap)
+    *namedatap = namedata;
+  return entries;
+}
+
+/* retrive header by rpmdbid, returns 0 if not found, -1 on error */
+static int
+getrpm_dbid(struct rpmdbstate *state, Id rpmdbid)
+{
+  Header h;
+  rpmdbMatchIterator mi;
+  unsigned int offset = rpmdbid;
+
+  if (state->dbenvopened != 1 && !opendbenv(state))
+    return -1;
+  mi = rpmtsInitIterator(state->ts, RPMDBI_PACKAGES, &offset, sizeof(offset));
+  h = rpmdbNextIterator(mi);
+  if (!h)
+    {
+      rpmdbFreeIterator(mi);
+      return 0;
+    }
+  if (!rpm_byrpmh(state, h))
+    {
+      rpmdbFreeIterator(mi);
+      return -1;
+    }
+  mi = rpmdbFreeIterator(mi);
+  return 1;
+}
+
+static int
+count_headers(struct rpmdbstate *state)
+{
+  int count;
+  rpmdbMatchIterator mi;
+
+  if (state->dbenvopened != 1 && !opendbenv(state))
+    return 0;
+  mi = rpmtsInitIterator(state->ts, RPMDBI_NAME, NULL, 0);
+  count = rpmdbGetIteratorCount(mi);
+  rpmdbFreeIterator(mi);
+  return count;
+}
+
+static int
+pkgdb_cursor_open(struct rpmdbstate *state)
+{
+  state->mi = rpmtsInitIterator(state->ts, RPMDBI_PACKAGES, NULL, 0);
+  return 0;
+}
+
+static void
+pkgdb_cursor_close(struct rpmdbstate *state)
+{
+  rpmdbFreeIterator(state->mi);
+  state->mi = 0;
+}
+
+static Id
+pkgdb_cursor_getrpm(struct rpmdbstate *state)
+{
+  Header h;
+  while ((h = rpmdbNextIterator(state->mi)))
+    {
+      Id dbid = rpmdbGetIteratorOffset(state->mi);
+      if (!rpm_byrpmh(state, h))
+       continue;
+      return dbid;
+    }
+  return 0;
+}
+
index 967984e..2c64bb6 100644 (file)
@@ -382,8 +382,8 @@ solv_xfopen_fd(const char *fn, int fd, const char *mode)
   if (suf && !strcmp(suf, ".gz"))
     return mygzfdopen(fd, simplemode);
 #else
-    return 0;
   if (suf && !strcmp(suf, ".gz"))
+    return 0;
 #endif
 #ifdef ENABLE_LZMA_COMPRESSION
   if (suf && !strcmp(suf, ".xz"))
index 1f19be1..aa72a8d 100644 (file)
@@ -86,6 +86,7 @@ static struct resultflags2str {
   { TESTCASE_RESULT_GENID,             "genid" },
   { TESTCASE_RESULT_REASON,            "reason" },
   { TESTCASE_RESULT_CLEANDEPS,         "cleandeps" },
+  { TESTCASE_RESULT_JOBS,              "jobs" },
   { 0, 0 }
 };
 
@@ -169,6 +170,10 @@ static struct selflags2str {
   { SELECTION_NOCASE, "nocase" },
   { SELECTION_SOURCE_ONLY, "sourceonly" },
   { SELECTION_WITH_SOURCE, "withsource" },
+  { SELECTION_SKIP_KIND, "skipkind" },
+  { SELECTION_MATCH_DEPSTR, "depstr" },
+  { SELECTION_WITH_DISABLED, "withdisabled" },
+  { SELECTION_WITH_BADARCH, "withbadarch" },
   { 0, 0 }
 };
 
@@ -1123,12 +1128,13 @@ testcase_str2job(Pool *pool, const char *str, Id *whatp)
 }
 
 static int
-addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue)
+addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue, int keyname)
 {
   Id job;
   int i, r;
   int selflags;
   Queue sel;
+  char *sp;
 
   for (i = 0; job2str[i].str; i++)
     if (!strcmp(pieces[0], job2str[i].str))
@@ -1149,9 +1155,18 @@ addselectionjob(Pool *pool, char **pieces, int npieces, Queue *jobqueue)
     }
   if (npieces < 4)
     return pool_error(pool, -1, "selstr2job: no selection flags");
-  selflags = str2selflags(pool, pieces[3]);
+  selflags = str2selflags(pool, pieces[npieces - 1]);
+  /* re-join pieces */
+  for (sp = pieces[2]; sp < pieces[npieces - 2]; sp++)
+    if (*sp == 0)
+      *sp = ' ';
   queue_init(&sel);
-  r = selection_make(pool, &sel, pieces[2], selflags);
+  if (keyname > 0)
+    r = selection_make_matchdeps(pool, &sel, pieces[2], selflags, keyname, 0);
+  else if (keyname < 0)
+    r = selection_make_matchdepid(pool, &sel, testcase_str2dep(pool, pieces[2]), selflags, -keyname, 0);
+  else
+    r = selection_make(pool, &sel, pieces[2], selflags);
   for (i = 0; i < sel.count; i += 2)
     queue_push2(jobqueue, job | sel.elements[i], sel.elements[i + 1]);
   queue_free(&sel);
@@ -1366,12 +1381,13 @@ testcase_add_testtags(Repo *repo, FILE *fp, int flags)
       if (*line != '=' || !line[1] || !line[2] || !line[3] || line[4] != ':')
        continue;
       tag = line[1] << 16 | line[2] << 8 | line[3];
+      /* tags that do not need a solvable */
       switch(tag)
-        {
+       {
        case 'V' << 16 | 'e' << 8 | 'r':
          tagsversion = atoi(line + 6);
          addselfprovides = tagsversion < 3 || strstr(line + 6, "addselfprovides") != 0;
-         break;
+         continue;
        case 'P' << 16 | 'k' << 8 | 'g':
          if (s)
            {
@@ -1390,7 +1406,15 @@ testcase_add_testtags(Repo *repo, FILE *fp, int flags)
            sp[2][-1] = '-';
          s->evr = makeevr(pool, sp[1]);
          s->arch = pool_str2id(pool, sp[3], 1);
+         continue;
+       default:
          break;
+       }
+      if (!s)
+       continue;
+      /* tags that need a solvable */
+      switch(tag)
+       {
        case 'S' << 16 | 'u' << 8 | 'm':
          repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 6);
          break;
@@ -2120,6 +2144,15 @@ testcase_solverresult(Solver *solv, int resultflags)
        }
       queue_free(&q);
     }
+  if ((resultflags & TESTCASE_RESULT_JOBS) != 0)
+    {
+      for (i = 0; i < solv->job.count; i += 2)
+       {
+         s = (char *)testcase_job2str(pool, solv->job.elements[i], solv->job.elements[i + 1]);
+         s = pool_tmpjoin(pool, "job ", s, 0);
+         strqueue_push(&sq, s);
+       }
+    }
   strqueue_sort(&sq);
   result = strqueue_join(&sq);
   strqueue_free(&sq);
@@ -2702,7 +2735,19 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
            }
          if (npieces >= 3 && !strcmp(pieces[2], "selection"))
            {
-             addselectionjob(pool, pieces + 1, npieces - 1, job);
+             addselectionjob(pool, pieces + 1, npieces - 1, job, 0);
+             continue;
+           }
+         if (npieces >= 4 && !strcmp(pieces[2], "selection_matchdeps"))
+           {
+             pieces[2] = pieces[1];
+             addselectionjob(pool, pieces + 2, npieces - 2, job, pool_str2id(pool, pieces[3], 1));
+             continue;
+           }
+         if (npieces >= 4 && !strcmp(pieces[2], "selection_matchdepid"))
+           {
+             pieces[2] = pieces[1];
+             addselectionjob(pool, pieces + 2, npieces - 2, job, -pool_str2id(pool, pieces[3], 1));
              continue;
            }
          /* rejoin */
index 2069637..387a506 100644 (file)
@@ -19,6 +19,7 @@
 #define TESTCASE_RESULT_GENID          (1 << 7)
 #define TESTCASE_RESULT_REASON         (1 << 8)
 #define TESTCASE_RESULT_CLEANDEPS      (1 << 9)
+#define TESTCASE_RESULT_JOBS           (1 << 10)
 
 /* reuse solver hack, testsolv use only */
 #define TESTCASE_RESULT_REUSE_SOLVER   (1 << 31)
index 17b1984..d29a812 100644 (file)
@@ -1,10 +1,27 @@
 -------------------------------------------------------------------
+Wed Jan 31 11:41:51 CET 2018 - mls@suse.de
+
+- new ENABLE_RPMDB_LIBRPM/ENABLE_RPMPKG_LIBRPM config options
+- new pool_set_whatprovides function to change the whatprovides
+  data
+- much improved selection code
+- bump version to 0.6.31
+
+-------------------------------------------------------------------
+Tue Oct 24 12:09:32 UTC 2017 - jengelh@inai.de
+
+- Update package descriptions and groups.
+  Replace old $RPM_* variables by macros.
+
+-------------------------------------------------------------------
 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
+- Fix splitprovides handling with addalreadyrecommended turned off
+  [bnc#1059065]
 - bump version to 0.6.30
 
 -------------------------------------------------------------------
index 6f153f0..92e9811 100644 (file)
@@ -1,7 +1,7 @@
 #
 # spec file for package libsolv
 #
-# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany.
+# Copyright (c) 2017 SUSE LINUX Products GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -102,24 +102,29 @@ BuildRequires:  xz-devel
 %endif
 
 
-Summary:        A new approach to package dependency solving
+Summary:        Package dependency solver using a satisfiability algorithm
 License:        BSD-3-Clause
 Group:          Development/Libraries/C and C++
 
 %description
-A new approach to package dependency solving
+libsolv is a library for solving packages and reading repositories.
+The solver uses a satisfiability algorithm.
 
 %if !%{with disable_shared}
 %package -n libsolv@LIBSOLV_SOVERSION@
-Summary:        A new approach to package dependency solving
-Group:          Development/Libraries/C and C++
+Summary:        Package dependency solver using a satisfiability algorithm
+Group:          System/Libraries
 
 %description -n libsolv@LIBSOLV_SOVERSION@
-A new approach to package dependency solving
+libsolv is a library for solving packages and reading repositories.
+It consists of two central blocks: Using a dictionary approach to
+store and retrieve package and dependency information, and, using a
+so-called satisfiability algorithm for resolving package
+dependencies.
 
 %endif
 %package devel
-Summary:        A new approach to package dependency solving
+Summary:        Development files for libsolv, a package solver
 Group:          Development/Libraries/C and C++
 %if !%{with disable_shared}
 Requires:       libsolv@LIBSOLV_SOVERSION@ = %version
@@ -128,18 +133,22 @@ Requires:       rpm-devel
 Conflicts:      libsatsolver-devel
 
 %description devel
-Development files for libsolv, a new approach to package dependency solving
+Development files for libsolv, a library for solving packages and
+reading repositories.
 
 %package tools
-Summary:        A new approach to package dependency solving
-Group:          Development/Libraries/C and C++
+Summary:        Utilities to work with .solv files
+Group:          System/Management
 Obsoletes:      satsolver-tools < 0.18
 Provides:       satsolver-tools = 0.18
 Conflicts:      satsolver-tools-obsolete
 Requires:       gzip bzip2 coreutils findutils
 
 %description tools
-A new approach to package dependency solving.
+libsolv is a library for solving packages and reading repositories.
+
+This subpackage contains utilities to create and work with the .solv
+files used by libsolv.
 
 %package demo
 Summary:        Applications demoing the libsolv library
@@ -161,7 +170,7 @@ Summary:        Ruby bindings for the libsolv library
 Group:          Development/Languages/Ruby
 
 %description -n ruby-solv
-Ruby bindings for sat solver.
+Ruby bindings for libsolv.
 
 %package -n python-solv
 %if 0%{?py_requires:1} && %{with python_binding}
@@ -171,14 +180,14 @@ Summary:        Python bindings for the libsolv library
 Group:          Development/Languages/Python
 
 %description -n python-solv
-Python bindings for sat solver.
+Python bindings for libsolv.
 
 %package -n python3-solv
 Summary:        Python3 bindings for the libsolv library
 Group:          Development/Languages/Python
 
 %description -n python3-solv
-Python3 bindings for sat solver.
+Python3 bindings for libsolv.
 
 %package -n perl-solv
 Requires:       perl = %{perl_version}
@@ -186,13 +195,13 @@ Summary:        Perl bindings for the libsolv library
 Group:          Development/Languages/Perl
 
 %description -n perl-solv
-Perl bindings for sat solver.
+Perl bindings for libsolv.
 
 %prep
 %setup -n libsolv-%{version}
 
 %build
-export CFLAGS="$RPM_OPT_FLAGS"
+export CFLAGS="%{optflags}"
 export CXXFLAGS="$CFLAGS"
 
 CMAKE_FLAGS=
@@ -227,16 +236,16 @@ cmake     $CMAKE_FLAGS \
 make %{?_smp_mflags}
 
 %install
-make DESTDIR=$RPM_BUILD_ROOT install
+make DESTDIR=%{buildroot} install
 %if 0%{?suse_version}
 %if %{with python_binding}
-pushd $RPM_BUILD_ROOT/%{python_sitearch}
+pushd %{buildroot}/%{python_sitearch}
 python %py_libdir/py_compile.py *.py
 python -O %py_libdir/py_compile.py *.py
 popd
 %endif
 %if %{with python3_binding}
-%py3_compile $RPM_BUILD_ROOT/%{python3_sitearch}
+%py3_compile %{buildroot}/%{python3_sitearch}
 %endif
 %endif
 %if %{with disable_shared}
@@ -244,9 +253,6 @@ popd
 export NO_BRP_STRIP_DEBUG=true
 %endif
 
-%clean
-rm -rf "$RPM_BUILD_ROOT"
-
 %if !%{with disable_shared}
 %post -n libsolv@LIBSOLV_SOVERSION@ -p /sbin/ldconfig
 
index 35516ba..e8951f6 100644 (file)
@@ -19,7 +19,8 @@ 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
-    fileprovides.c diskusage.c suse.c solver_util.c cleandeps.c)
+    fileprovides.c diskusage.c suse.c solver_util.c cleandeps.c
+    userinstalled.c)
 
 SET (libsolv_HEADERS
     bitmap.h evr.h hash.h policy.h poolarch.h poolvendor.h pool.h
index 1bf1666..e004bf2 100644 (file)
@@ -63,38 +63,48 @@ map_grow(Map *m, int n)
 void
 map_and(Map *t, Map *s)
 {
-    unsigned char *ti, *si, *end;
-    ti = t->map;
-    si = s->map;
-    end = ti + (t->size < s->size ? t->size : s->size);
-    while (ti < end)
-       *ti++ &= *si++;
+  unsigned char *ti, *si, *end;
+  ti = t->map;
+  si = s->map;
+  end = ti + (t->size < s->size ? t->size : s->size);
+  while (ti < end)
+    *ti++ &= *si++;
 }
 
 /* bitwise-ors maps t and s, stores the result in t. */
 void
 map_or(Map *t, Map *s)
 {
-    unsigned char *ti, *si, *end;
-    if (t->size < s->size)
-      map_grow(t, s->size << 3);
-    ti = t->map;
-    si = s->map;
-    end = ti + (t->size < s->size ? t->size : s->size);
-    while (ti < end)
-       *ti++ |= *si++;
+  unsigned char *ti, *si, *end;
+  if (t->size < s->size)
+    map_grow(t, s->size << 3);
+  ti = t->map;
+  si = s->map;
+  end = ti + (t->size < s->size ? t->size : s->size);
+  while (ti < end)
+    *ti++ |= *si++;
 }
 
 /* remove all set bits in s from t. */
 void
 map_subtract(Map *t, Map *s)
 {
-    unsigned char *ti, *si, *end;
-    ti = t->map;
-    si = s->map;
-    end = ti + (t->size < s->size ? t->size : s->size);
-    while (ti < end)
-       *ti++ &= ~*si++;
+  unsigned char *ti, *si, *end;
+  ti = t->map;
+  si = s->map;
+  end = ti + (t->size < s->size ? t->size : s->size);
+  while (ti < end)
+    *ti++ &= ~*si++;
+}
+
+void
+map_invertall(Map *m)
+{
+  unsigned char *ti, *end;
+  ti = m->map;
+  end = ti + m->size;
+  while (ti < end)
+    *ti++ ^= 0xff;
 }
 
 /* EOF */
index 5784e6c..0050a6a 100644 (file)
@@ -33,6 +33,8 @@ typedef struct _Map {
 #define MAPCLR(m, n) ((m)->map[(n) >> 3] &= ~(1 << ((n) & 7)))
 /* test bit */
 #define MAPTST(m, n) ((m)->map[(n) >> 3] & (1 << ((n) & 7)))
+/* clear some bits at a position */
+#define MAPCLR_AT(m, n) ((m)->map[(n) >> 3] = 0)
 
 extern void map_init(Map *m, int n);
 extern void map_init_clone(Map *t, Map *s);
@@ -41,6 +43,7 @@ extern void map_free(Map *m);
 extern void map_and(Map *t, Map *s);
 extern void map_or(Map *t, Map *s);
 extern void map_subtract(Map *t, Map *s);
+extern void map_invertall(Map *m);
 
 static inline void map_empty(Map *m)
 {
@@ -62,6 +65,10 @@ static inline int map_tst(Map *m, int n)
 {
   return MAPTST(m, n);
 }
+static inline void map_clr_at(Map *m, int n)
+{
+  MAPCLR_AT(m, n);
+}
 
 #ifdef __cplusplus
 }
index 337cd19..64191d4 100644 (file)
@@ -427,3 +427,9 @@ SOLV_1.1 {
                pool_best_solvables;
                solver_get_cleandeps;
 } SOLV_1.0;
+
+SOLV_1.2 {
+               map_invertall;
+               pool_set_whatprovides;
+               selection_subtract;
+} SOLV_1.1;
index b596ace..ba5e799 100644 (file)
@@ -859,7 +859,7 @@ pool_match_dep(Pool *pool, Id d1, Id d2)
   if (!ISRELDEP(d1))
     {
       if (!ISRELDEP(d2))
-       return 0;
+       return 0;       /* cannot match as d1 != d2 */
       rd2 = GETRELDEP(pool, d2);
       return pool_match_dep(pool, d1, rd2->name);
     }
@@ -1410,7 +1410,8 @@ pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr)
        continue;
       if (evr && rd->evr != evr)
        continue;
-      pool->whatprovides_rel[d] = 0;
+      if (pool->whatprovides_rel[d])
+        pool_set_whatprovides(pool, MAKERELDEP(d), 0);
     }
 }
 
@@ -1516,6 +1517,9 @@ pool_error(Pool *pool, int ret, const char *format, ...)
 {
   va_list args;
   int l;
+
+  if (!pool)
+    return ret;
   va_start(args, format);
   if (!pool->errstr)
     {
@@ -1862,24 +1866,66 @@ pool_lookup_deltalocation(Pool *pool, Id entry, unsigned int *medianrp)
   return loc;
 }
 
+void
+pool_set_whatprovides(Pool *pool, Id id, Id providers)
+{
+  int d, nrels = pool->nrels;
+  Reldep *rd;
+  Map m;
+
+  /* set new entry */
+  if (ISRELDEP(id))
+    {
+      d = GETRELID(id);
+      pool->whatprovides_rel[d] = providers;
+      d++;
+    }
+  else
+    {
+      pool->whatprovides[id] = providers;
+      if (id < pool->whatprovidesauxoff)
+       pool->whatprovidesaux[id] = 0;  /* sorry */
+      d = 1;
+    }
+  if (!pool->whatprovides_rel)
+    return;
+  /* clear cache of all rels that use it */
+  map_init(&m, 0);
+  for (rd = pool->rels + d; d < nrels; d++, rd++)
+    {
+      if (rd->name == id || rd->evr == id ||
+         (m.size && ISRELDEP(rd->name) && MAPTST(&m, GETRELID(rd->name))) || 
+         (m.size && ISRELDEP(rd->evr)  && MAPTST(&m, GETRELID(rd->evr))))
+       {
+         pool->whatprovides_rel[d] = 0;        /* clear cache */
+         if (!m.size)
+           map_init(&m, nrels);
+         MAPSET(&m, d);
+       }
+    }
+  map_free(&m);
+}
+
 static void
 add_new_provider(Pool *pool, Id id, Id p)
 {
   Queue q;
   Id *pp;
 
+  /* find whatprovides entry */
   while (ISRELDEP(id))
     {
       Reldep *rd = GETRELDEP(pool, id);
       id = rd->name;
     }
 
+  /* add new provider to existing list keeping it sorted */
   queue_init(&q);
   for (pp = pool->whatprovidesdata + pool->whatprovides[id]; *pp; pp++)
     {
       if (*pp == p)
        {
-         queue_free(&q);
+         queue_free(&q);       /* already have it */
          return;
        }
       if (*pp > p)
@@ -1891,9 +1937,7 @@ add_new_provider(Pool *pool, Id id, Id p)
     }
   if (p)
     queue_push(&q, p);
-  pool->whatprovides[id] = pool_queuetowhatprovides(pool, &q);
-  if (id < pool->whatprovidesauxoff)
-    pool->whatprovidesaux[id] = 0;     /* sorry */
+  pool_set_whatprovides(pool, id, pool_queuetowhatprovides(pool, &q));
   queue_free(&q);
 }
 
@@ -1920,9 +1964,7 @@ pool_add_fileconflicts_deps(Pool *pool, Queue *conflicts)
        continue;
       s->provides = repo_addid_dep(s->repo, s->provides, id, SOLVABLE_FILEMARKER);
       if (pool->whatprovides)
-       add_new_provider(pool, fn, p);
-      if (pool->whatprovides_rel)
-       pool->whatprovides_rel[GETRELID(id)] = 0;       /* clear cache */
+       add_new_provider(pool, id, p);
       s = pool->solvables + q;
       if (!s->repo)
        continue;
index f6a5493..f6573af 100644 (file)
@@ -344,6 +344,8 @@ static inline Id *pool_whatprovides_ptr(Pool *pool, Id d)
 
 void pool_whatmatchesdep(Pool *pool, Id keyname, Id dep, Queue *q, int marker);
 void pool_whatcontainsdep(Pool *pool, Id keyname, Id dep, Queue *q, int marker);
+void pool_set_whatprovides(Pool *pool, Id id, Id providers);
+
 
 /* search the pool. the following filters are available:
  *   p     - search just this solvable
index 2ffb6f9..7933f7c 100644 (file)
@@ -24,7 +24,6 @@
 #include "evr.h"
 #include "solverdebug.h"
 
-
 /**********************************************************************************/
 
 /* a problem is an item on the solver's problem list. It can either be >0, in that
  * consisting of multiple job rules.
  */
 
-void
+static void
 solver_disableproblem(Solver *solv, Id v)
 {
-  Rule *r;
   int i;
   Id *jp;
 
@@ -62,30 +60,30 @@ solver_disableproblem(Solver *solv, Id v)
          return;
        }
       solver_disablerule(solv, solv->rules + v);
-#if 0
-      /* XXX: doesn't work */
-      if (v >= solv->updaterules && v < solv->updaterules_end)
-       {
-         /* enable feature rule if we disabled the update rule */
-         r = solv->rules + (v - solv->updaterules + solv->featurerules);
-         if (r->p)
-           solver_enablerule(solv, r);
-       }
-#endif
       return;
     }
   v = -(v + 1);
   jp = solv->ruletojob.elements;
-  for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++, jp++)
+  if (solv->bestrules_pkg)
+    {
+      int ni = solv->bestrules_up - solv->bestrules;
+      for (i = 0; i < ni; i++)
+       {
+         int j = solv->bestrules_pkg[i];
+         if (j < 0 && jp[-j - solv->jobrules] == v)
+           solver_disablerule(solv, solv->rules + solv->bestrules + i);
+       }
+    }
+  for (i = solv->jobrules; i < solv->jobrules_end; i++, jp++)
     if (*jp == v)
-      solver_disablerule(solv, r);
+      solver_disablerule(solv, solv->rules + i);
 }
 
 /*-------------------------------------------------------------------
  * enableproblem
  */
 
-void
+static void
 solver_enableproblem(Solver *solv, Id v)
 {
   Rule *r;
@@ -133,9 +131,174 @@ solver_enableproblem(Solver *solv, Id v)
     }
   v = -(v + 1);
   jp = solv->ruletojob.elements;
-  for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++, jp++)
+  if (solv->bestrules_pkg)
+    {
+      int ni = solv->bestrules_up - solv->bestrules;
+      for (i = 0; i < ni; i++)
+       {
+         int j = solv->bestrules_pkg[i];
+         if (j < 0 && jp[-j - solv->jobrules] == v)
+           solver_enablerule(solv, solv->rules + solv->bestrules + i);
+       }
+    }
+  for (i = solv->jobrules; i < solv->jobrules_end; i++, jp++)
     if (*jp == v)
-      solver_enablerule(solv, r);
+      solver_enablerule(solv, solv->rules + i);
+}
+
+
+/*-------------------------------------------------------------------
+ * turn a problem rule into a problem id by normalizing it
+ */
+static Id
+solver_ruletoproblem(Solver *solv, Id rid)
+{
+  if (rid >= solv->jobrules && rid < solv->jobrules_end)
+    rid = -(solv->ruletojob.elements[rid - solv->jobrules] + 1);
+  else if (rid >= solv->bestrules && rid < solv->bestrules_up && solv->bestrules_pkg[rid - solv->bestrules] < 0)
+    rid = -(solv->ruletojob.elements[-solv->bestrules_pkg[rid - solv->bestrules] - solv->jobrules] + 1);
+  else if (rid > solv->infarchrules && rid < solv->infarchrules_end)
+    {
+      Pool *pool = solv->pool;
+      Id name = pool->solvables[-solv->rules[rid].p].name;
+      while (rid > solv->infarchrules && pool->solvables[-solv->rules[rid - 1].p].name == name)
+        rid--;
+    }
+  else if (rid > solv->duprules && rid < solv->duprules_end)
+    {
+      Pool *pool = solv->pool;
+      Id name = pool->solvables[-solv->rules[rid].p].name;
+      while (rid > solv->duprules && pool->solvables[-solv->rules[rid - 1].p].name == name)
+        rid--;
+    }
+  return rid;
+}
+
+/*-------------------------------------------------------------------
+ * when the solver runs into a problem, it needs to disable all
+ * involved non-pkg rules and record the rules for solution
+ * generation.
+ */
+void
+solver_recordproblem(Solver *solv, Id rid)
+{
+  Id v = solver_ruletoproblem(solv, rid);
+  /* return if problem already countains our rule */
+  if (solv->problems.count)
+    {
+      int i;
+      for (i = solv->problems.count - 1; i >= 0; i--)
+        if (solv->problems.elements[i] == 0)    /* end of last problem reached? */
+          break;
+        else if (solv->problems.elements[i] == v)
+          return;
+    }
+  queue_push(&solv->problems, v);
+}
+
+/*-------------------------------------------------------------------
+ * this is called when a problem is solved by disabling a rule.
+ * It calls disableproblem and then re-enables policy rules
+ */
+void
+solver_fixproblem(Solver *solv, Id rid)
+{
+  Id v = solver_ruletoproblem(solv, rid);
+  solver_disableproblem(solv, v);
+  if (v < 0)
+    solver_reenablepolicyrules(solv, -v);
+}
+
+/*-------------------------------------------------------------------
+ * disable a set of problems
+ */
+void
+solver_disableproblemset(Solver *solv, int start)
+{
+  int i;
+  for (i = start + 1; i < solv->problems.count - 1; i++)
+    solver_disableproblem(solv, solv->problems.elements[i]);
+}
+
+/*-------------------------------------------------------------------
+ * try to fix a problem by auto-uninstalling packages
+ */
+Id
+solver_autouninstall(Solver *solv, int start)
+{
+  Pool *pool = solv->pool;
+  int i;
+  int lastfeature = 0, lastupdate = 0;
+  Id v;
+  Id extraflags = -1;
+  Map *m = 0;
+
+  if (!solv->allowuninstall && !solv->allowuninstall_all)
+    {
+      if (!solv->allowuninstallmap.size)
+       return 0;               /* why did we get called? */
+      m = &solv->allowuninstallmap;
+    }
+  for (i = start + 1; i < solv->problems.count - 1; i++)
+    {
+      v = solv->problems.elements[i];
+      if (v < 0)
+       extraflags &= solv->job.elements[-v - 1];
+      if (v >= solv->updaterules && v < solv->updaterules_end)
+       {
+         Rule *r;
+         if (m && !MAPTST(m, v - solv->updaterules))
+           continue;
+         /* check if identical to feature rule, we don't like that (except for orphans) */
+         r = solv->rules + solv->featurerules + (v - solv->updaterules);
+         if (!r->p)
+           {
+             /* update rule == feature rule */
+             if (v > lastfeature)
+               lastfeature = v;
+             /* prefer orphaned packages in dup mode */
+             if (solv->keep_orphans)
+               {
+                 r = solv->rules + v;
+                 if (!r->d && !r->w2 && r->p == (solv->installed->start + (v - solv->updaterules)))
+                   {
+                     lastfeature = v;
+                     lastupdate = 0;
+                     break;
+                   }
+               }
+             continue;
+           }
+         if (v > lastupdate)
+           lastupdate = v;
+       }
+    }
+  if (!lastupdate && !lastfeature)
+    return 0;
+  v = lastupdate ? lastupdate : lastfeature;
+  POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "allowuninstall disabling ");
+  solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + v);
+  /* should really be solver_fixproblem, but we know v is an update/feature rule */
+  solver_disableproblem(solv, v);
+  if (extraflags != -1 && (extraflags & SOLVER_CLEANDEPS) != 0 && solv->cleandepsmap.size)
+    {
+      /* add the package to the updatepkgs list, this will automatically turn
+       * on cleandeps mode */
+      Id p = solv->rules[v].p;
+      if (!solv->cleandeps_updatepkgs)
+       {
+         solv->cleandeps_updatepkgs = solv_calloc(1, sizeof(Queue));
+         queue_init(solv->cleandeps_updatepkgs);
+       }
+      if (p > 0)
+       {
+         int oldupdatepkgscnt = solv->cleandeps_updatepkgs->count;
+          queue_pushunique(solv->cleandeps_updatepkgs, p);
+         if (solv->cleandeps_updatepkgs->count != oldupdatepkgscnt)
+           solver_disablepolicyrules(solv);
+       }
+    }
+  return v;
 }
 
 
@@ -202,7 +365,6 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti
   queue_empty(refined);
   if (!essentialok && sug < 0 && (solv->job.elements[-sug - 1] & SOLVER_ESSENTIAL) != 0)
     return;
-  queue_init(&disabled);
   queue_push(refined, sug);
 
   /* re-enable all problem rules with the exception of "sug"(gestion) */
@@ -211,10 +373,14 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti
   for (i = 0; problem[i]; i++)
     if (problem[i] != sug)
       solver_enableproblem(solv, problem[i]);
-
   if (sug < 0)
     solver_reenablepolicyrules(solv, -sug);
-  else if (sug >= solv->updaterules && sug < solv->updaterules_end)
+
+  /* here is where the feature rules come into play: if we disabled an
+   * update rule, we enable the corresponding feature rule if there is
+   * one. We do this to make the solver downgrade packages instead of
+   * deinstalling them */
+  if (sug >= solv->updaterules && sug < solv->updaterules_end)
     {
       /* enable feature rule */
       Rule *r = solv->rules + solv->featurerules + (sug - solv->updaterules);
@@ -224,11 +390,15 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti
 
   enableweakrules(solv);
 
+  /* disabled contains all of the rules we disabled in the refinement process */
+  queue_init(&disabled);
   for (;;)
     {
-      int njob, nfeature, nupdate, pass;
+      int nother, nfeature, nupdate, pass;
       queue_empty(&solv->problems);
       solver_reset(solv);
+      /* we set disablerules to zero because we are only interested in
+       * the first problem and we don't want the solver to disable the problems */
       solver_run_sat(solv, 0, 0);
 
       if (!solv->problems.count)
@@ -237,16 +407,18 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti
          break;                /* great, no more problems */
        }
       disabledcnt = disabled.count;
-      /* start with 1 to skip over proof index */
-      njob = nfeature = nupdate = 0;
+      nother = nfeature = nupdate = 0;
       for (pass = 0; pass < 2; pass++)
        {
+         /* start with 1 to skip over proof index */
          for (i = 1; i < solv->problems.count - 1; i++)
            {
              /* ignore solutions in refined */
              v = solv->problems.elements[i];
              if (v == 0)
                break;  /* end of problem reached */
+             if (!essentialok && v < 0 && (solv->job.elements[-v - 1] & SOLVER_ESSENTIAL) != 0)
+               continue;       /* not that one! */
              if (sug != v)
                {
                  /* check if v is in the given problems list
@@ -260,14 +432,10 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti
                }
              if (v >= solv->featurerules && v < solv->featurerules_end)
                nfeature++;
-             else if (v > 0)
+             else if (v > solv->updaterules && v < solv->updaterules_end)
                nupdate++;
              else
-               {
-                 if (!essentialok && (solv->job.elements[-v - 1] & SOLVER_ESSENTIAL) != 0)
-                   continue;   /* not that one! */
-                 njob++;
-               }
+               nother++;
              queue_push(&disabled, v);
            }
          if (disabled.count != disabledcnt)
@@ -280,7 +448,7 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti
          refined->count = 0;
          break;
        }
-      if (!njob && nupdate && nfeature)
+      if (!nother && nupdate && nfeature)
        {
          /* got only update rules, filter out feature rules */
          POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "throwing away feature rules\n");
@@ -300,14 +468,14 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti
          if (!nfeature && v != sug)
            queue_push(refined, v);     /* do not record feature rules */
          solver_disableproblem(solv, v);
+         if (v < 0)
+           solver_reenablepolicyrules(solv, -v);
          if (v >= solv->updaterules && v < solv->updaterules_end)
            {
              Rule *r = solv->rules + (v - solv->updaterules + solv->featurerules);
              if (r->p)
                solver_enablerule(solv, r);     /* enable corresponding feature rule */
            }
-         if (v < 0)
-           solver_reenablepolicyrules(solv, -v);
        }
       else
        {
@@ -339,6 +507,7 @@ refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essenti
   for (i = 0; i < disabled.count; i++)
     solver_enableproblem(solv, disabled.elements[i]);
   queue_free(&disabled);
+
   /* reset policy rules */
   for (i = 0; problem[i]; i++)
     solver_enableproblem(solv, problem[i]);
index e5b2279..63319d6 100644 (file)
@@ -28,8 +28,11 @@ struct _Solver;
 #define SOLVER_SOLUTION_BEST            (-3)
 #define SOLVER_SOLUTION_POOLJOB         (-4)
 
-void solver_disableproblem(struct _Solver *solv, Id v);
-void solver_enableproblem(struct _Solver *solv, Id v);
+void solver_recordproblem(struct _Solver *solv, Id rid);
+void solver_fixproblem(struct _Solver *solv, Id rid);
+Id solver_autouninstall(struct _Solver *solv, int start);
+void solver_disableproblemset(struct _Solver *solv, int start);
+
 int solver_prepare_solutions(struct _Solver *solv);
 
 unsigned int solver_problem_count(struct _Solver *solv);
index a63999a..9dcbcca 100644 (file)
@@ -98,6 +98,15 @@ static inline int pool_disabled_solvable(const Pool *pool, Solvable *s)
   return 0;
 }
 
+static inline int pool_badarch_solvable(const Pool *pool, Solvable *s)
+{
+  if (!s->arch)
+    return 1;
+  if (pool->id2arch && (s->arch > pool->lastarch || !pool->id2arch[s->arch]))
+    return 1;
+  return 0;
+}
+
 static inline int pool_installable(const Pool *pool, Solvable *s)
 {
   if (!s->arch || s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
index b5f3e3e..cf368e4 100644 (file)
@@ -1466,7 +1466,7 @@ disableupdaterule(Solver *solv, Id p)
     {
       int i, ni;
       ni = solv->bestrules_end - solv->bestrules;
-      for (i = 0; i < ni; i++)
+      for (i = solv->bestrules_up - solv->bestrules; i < ni; i++)
        if (solv->bestrules_pkg[i] == p)
          solver_disablerule(solv, solv->rules + solv->bestrules + i);
     }
@@ -1509,7 +1509,7 @@ reenableupdaterule(Solver *solv, Id p)
     {
       int i, ni;
       ni = solv->bestrules_end - solv->bestrules;
-      for (i = 0; i < ni; i++)
+      for (i = solv->bestrules_up - solv->bestrules; i < ni; i++)
        if (solv->bestrules_pkg[i] == p)
          solver_enablerule(solv, solv->rules + solv->bestrules + i);
     }
@@ -1922,6 +1922,21 @@ solver_createdupmaps(Solver *solv)
     }
   if (solv->dupinvolvedmap.size)
     MAPCLR(&solv->dupinvolvedmap, SYSTEMSOLVABLE);
+  /* set update for all involved installed packages. We need to do
+   * this before creating the update rules */
+  if (solv->dupinvolvedmap_all)
+    solv->updatemap_all = 1;
+  else if (installed && !solv->updatemap_all && solv->dupinvolvedmap.size)
+    {
+      FOR_REPO_SOLVABLES(installed, p, s)
+       {
+         if (!MAPTST(&solv->dupinvolvedmap, p))
+           continue;
+         if (!solv->updatemap.size)
+           map_grow(&solv->updatemap, installed->end - installed->start);
+         MAPSET(&solv->updatemap, p - installed->start);
+       }
+    }
 }
 
 void
@@ -1943,8 +1958,6 @@ solver_addduprules(Solver *solv, Map *addedmap)
   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))
@@ -1964,12 +1977,6 @@ solver_addduprules(Solver *solv, Map *addedmap)
            continue;
          if (installed && ps->repo == installed)
            {
-             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;
@@ -3257,36 +3264,47 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
     {
       for (i = 0; i < solv->job.count; i += 2)
        {
-         if ((solv->job.elements[i] & (SOLVER_JOBMASK | SOLVER_FORCEBEST)) == (SOLVER_INSTALL | SOLVER_FORCEBEST))
+         Id how = solv->job.elements[i];
+         if ((how & (SOLVER_JOBMASK | SOLVER_FORCEBEST)) == (SOLVER_INSTALL | SOLVER_FORCEBEST))
            {
              int j;
              Id p2, pp2;
              for (j = 0; j < solv->ruletojob.count; j++)
-               if (solv->ruletojob.elements[j] == i)
-                 break;
-             if (j == solv->ruletojob.count)
-               continue;
-             r = solv->rules + solv->jobrules + j;
-             queue_empty(&q);
-             FOR_RULELITERALS(p2, pp2, r)
-               if (p2 > 0)
-                 queue_push(&q, p2);
-             if (!q.count)
-               continue;       /* orphaned */
-             /* select best packages, just look at prio and version */
-             oldcnt = q.count;
-             policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND);
-             if (q.count == oldcnt)
-               continue;       /* nothing filtered */
-             p2 = queue_shift(&q);
-             if (q.count < 2)
-               solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0);
-             else
-               solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q));
-             queue_push(&r2pkg, -(solv->jobrules + j));
+               {
+                 if (solv->ruletojob.elements[j] != i)
+                   continue;
+                 r = solv->rules + solv->jobrules + j;
+                 queue_empty(&q);
+                 queue_empty(&q2);
+                 FOR_RULELITERALS(p2, pp2, r)
+                   {
+                     if (p2 > 0)
+                       queue_push(&q, p2);
+                     else if (p2 < 0)
+                       queue_push(&q2, p2);
+                   }
+                 if (!q.count)
+                   continue;   /* orphaned */
+                 /* select best packages, just look at prio and version */
+                 oldcnt = q.count;
+                 policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND);
+                 if (q.count == oldcnt)
+                   continue;   /* nothing filtered */
+                 if (q2.count)
+                   queue_insertn(&q, 0, q2.count, q2.elements);
+                 p2 = queue_shift(&q);
+                 if (q.count < 2)
+                   solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0);
+                 else
+                   solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q));
+                 if ((how & SOLVER_WEAK) != 0)
+                   queue_push(&solv->weakruleq, solv->nrules - 1);
+                 queue_push(&r2pkg, -(solv->jobrules + j));
+               }
            }
        }
     }
+  solv->bestrules_up = solv->nrules;
 
   if (installed && (solv->bestupdatemap_all || solv->bestupdatemap.size))
     {
@@ -3398,6 +3416,8 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
 
 
 /* yumobs rule handling */
+/* note that we use pool->obsoleteusescolors || pool->implicitobsoleteusescolors
+ * like in policy_findupdatepackages */
 
 static void
 find_obsolete_group(Solver *solv, Id obs, Queue *q)
@@ -3422,7 +3442,7 @@ find_obsolete_group(Solver *solv, Id obs, Queue *q)
          Id obs2, *obsp2;
          if (!os->obsoletes)
            continue;
-         if (pool->obsoleteusescolors && !pool_colormatch(pool, s2, os))
+         if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s2, os))
            continue;
          obsp2 = os->repo->idarraydata + os->obsoletes; 
          while ((obs2 = *obsp2++) != 0)
@@ -3440,7 +3460,7 @@ find_obsolete_group(Solver *solv, Id obs, Queue *q)
            continue;
          if (!os->obsoletes)
            continue;
-         if (pool->obsoleteusescolors && !pool_colormatch(pool, s2, os))
+         if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s2, os))
            continue;
          obsp2 = os->repo->idarraydata + os->obsoletes; 
          while ((obs2 = *obsp2++) != 0)
@@ -3539,7 +3559,7 @@ printf("checking yumobs for %s\n", pool_solvable2str(pool, s));
                    continue;
                  if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
                    continue;
-                 if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
+                 if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s, s2))
                    continue;
                  queue_pushunique(&qo, obs);
                  break;
index 16fe3a7..6ca72e5 100644 (file)
@@ -37,6 +37,7 @@ str2archid(Pool *pool, const char *arch)
   return id;
 }
 
+/* remove empty jobs from the selection */
 static void
 selection_prune(Pool *pool, Queue *selection)
 {
@@ -155,33 +156,49 @@ selection_flatten(Pool *pool, Queue *selection)
     }
 }
 
+/* only supports simple rels plus REL_ARCH */
+static int
+match_nevr_rel(Pool *pool, Solvable *s, Id rflags, Id revr)
+{
+  if (rflags == REL_ARCH)
+    {
+      if (s->arch != revr) 
+       {
+         if (revr != ARCH_SRC || s->arch != ARCH_NOSRC)
+           return 0;
+       }
+      return 1;
+    }
+  if (rflags > 7)
+    return 0;
+  return pool_intersect_evrs(pool, REL_EQ, s->evr, rflags, revr);
+}
+
+/* only supports simple rels plus REL_ARCH */
 static void
-selection_filter_rel(Pool *pool, Queue *selection, Id relflags, Id relevr)
+selection_filter_rel_noprune(Pool *pool, Queue *selection, Id relflags, Id relevr)
 {
   int i;
 
+  if (!selection->count)
+    return;
+
   for (i = 0; i < selection->count; i += 2)
     {
       Id select = selection->elements[i] & SOLVER_SELECTMASK;
       Id id = selection->elements[i + 1];
       if (select == SOLVER_SOLVABLE || select == SOLVER_SOLVABLE_ONE_OF)
        {
-         /* done by selection_addsrc, currently implies SELECTION_NAME */
+         /* done by selection_addextra, currently implies SELECTION_NAME */
          Queue q;
          Id p, pp;
-         Id rel = 0, relname = 0;
          int miss = 0;
 
          queue_init(&q);
          FOR_JOB_SELECT(p, pp, select, id)
            {
              Solvable *s = pool->solvables + p;
-             if (!rel || s->name != relname)
-               {
-                 relname = s->name;
-                 rel = pool_rel2id(pool, relname, relevr, relflags, 1);
-               }
-             if (pool_match_nevr(pool, s, rel))
+             if (match_nevr_rel(pool, s, relflags, relevr))
                queue_push(&q, p);
              else
                miss = 1;
@@ -213,31 +230,48 @@ selection_filter_rel(Pool *pool, Queue *selection, Id relflags, Id relevr)
          selection->elements[i + 1] = pool_rel2id(pool, id, relevr, relflags, 1);
        }
       else
-       continue;       /* actually internal error */
+       continue;       /* actually cannot happen */
+
+      /* now add the setflags we gained */
       if (relflags == REL_ARCH)
-        selection->elements[i] |= SOLVER_SETARCH;
+       selection->elements[i] |= SOLVER_SETARCH;
       if (relflags == REL_EQ && select != SOLVER_SOLVABLE_PROVIDES)
-        {
+       {
          if (pool->disttype == DISTTYPE_DEB)
-            selection->elements[i] |= SOLVER_SETEVR;   /* debian can't match version only like rpm */
+           selection->elements[i] |= SOLVER_SETEVR;    /* debian can't match version only like rpm */
          else
            {
              const char *rel =  strrchr(pool_id2str(pool, relevr), '-');
              selection->elements[i] |= rel ? SOLVER_SETEVR : SOLVER_SETEV;
            }
-        }
+       }
     }
+}
+
+/* only supports simple rels plus REL_ARCH */
+/* prunes empty jobs */
+static void
+selection_filter_rel(Pool *pool, Queue *selection, Id relflags, Id relevr)
+{
+  selection_filter_rel_noprune(pool, selection, relflags, relevr);
   selection_prune(pool, selection);
 }
 
+/* limit a selection to to repository */
+/* prunes empty jobs */
 static void
-selection_filter_installed(Pool *pool, Queue *selection)
+selection_filter_repo(Pool *pool, Queue *selection, Repo *repo)
 {
   Queue q;
   int i, j;
 
-  if (!pool->installed)
-    queue_empty(selection);
+  if (!selection->count)
+    return;
+  if (!repo)
+    {
+      queue_empty(selection);
+      return;
+    }
   queue_init(&q);
   for (i = j = 0; i < selection->count; i += 2)
     {
@@ -246,11 +280,11 @@ selection_filter_installed(Pool *pool, Queue *selection)
       if (select == SOLVER_SOLVABLE_ALL)
        {
          select = SOLVER_SOLVABLE_REPO;
-         id = pool->installed->repoid;
+         id = repo->repoid;
        }
       else if (select == SOLVER_SOLVABLE_REPO)
        {
-         if (id != pool->installed->repoid)
+         if (id != repo->repoid)
            select = 0;
        }
       else
@@ -260,7 +294,7 @@ selection_filter_installed(Pool *pool, Queue *selection)
          queue_empty(&q);
          FOR_JOB_SELECT(p, pp, select, id)
            {
-             if (pool->solvables[p].repo != pool->installed)
+             if (pool->solvables[p].repo != repo)
                bad = 1;
              else
                queue_push(&q, p);
@@ -268,7 +302,7 @@ selection_filter_installed(Pool *pool, Queue *selection)
          if (bad || !q.count)
            {
              if (!q.count)
-               select = 0;
+               select = 0;             /* prune empty jobs */
              else if (q.count == 1)
                {
                  select = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
@@ -281,58 +315,138 @@ selection_filter_installed(Pool *pool, Queue *selection)
                }
            }
        }
-      if (select)
+      if (!select)
+       continue;       /* job is now empty */
+      if (select == SOLVER_SOLVABLE_REPO)
        {
-         selection->elements[j++] = select | (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SETREPO;
-         selection->elements[j++] = id;
+         Id p;
+         Solvable *s;
+         FOR_REPO_SOLVABLES(repo, p, s)
+           break;
+         if (!p)
+           continue;   /* repo is empty */
        }
+      selection->elements[j++] = select | (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SETREPO;
+      selection->elements[j++] = id;
     }
   queue_truncate(selection, j);
   queue_free(&q);
 }
 
+
+static int
+matchprovides(Pool *pool, Solvable *s, Id dep)
+{
+  Id id, *idp;
+  idp = s->repo->idarraydata + s->provides;
+  while ((id = *idp++) != 0)
+    if (pool_match_dep(pool, id, dep))
+      return 1;
+  return 0;
+}
+
+/* change a SOLVER_SOLVABLE_NAME/PROVIDES selection to something that also includes
+ * extra packages.
+ * extra packages are: src, badarch, disabled
+ */
 static void
-selection_addsrc(Pool *pool, Queue *selection, int flags)
+selection_addextra(Pool *pool, Queue *selection, int flags)
 {
   Queue q;
-  Id p, name;
-  int i, havesrc;
+  Id p, pp, dep;
+  int i, isextra, haveextra, doprovides;
 
   if ((flags & SELECTION_INSTALLED_ONLY) != 0)
-    return;    /* sources can't be installed */
+    flags &= ~SELECTION_WITH_SOURCE;
+
+  if (!(flags & (SELECTION_WITH_SOURCE | SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH)))
+    return;    /* nothing to add */
+
   queue_init(&q);
   for (i = 0; i < selection->count; i += 2)
     {
-      if (selection->elements[i] != SOLVER_SOLVABLE_NAME)
+      if (selection->elements[i] == SOLVER_SOLVABLE_NAME)
+       doprovides = 0;
+      else if (selection->elements[i] == SOLVER_SOLVABLE_PROVIDES)
+       doprovides = 1;
+      else
        continue;
-      name = selection->elements[i + 1];
-      havesrc = 0;
+      dep = selection->elements[i + 1];
+      haveextra = 0;
       queue_empty(&q);
+      if (doprovides)
+       {
+         /* first put all non-extra packages on the queue */
+         FOR_PROVIDES(p, pp, dep)
+           {
+             if ((flags & SELECTION_INSTALLED_ONLY) != 0 && pool->solvables[p].repo != pool->installed)
+               continue;
+             queue_push(&q, p);
+           }
+       }
       FOR_POOL_SOLVABLES(p)
        {
          Solvable *s = pool->solvables + p;
-         if (s->name != name)
+         if (!doprovides && !pool_match_nevr(pool, s, dep))
            continue;
+         if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+           continue;
+         isextra = 0;
          if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
            {
+             if (!(flags & SELECTION_WITH_SOURCE) && !(flags & SELECTION_SOURCE_ONLY))
+               continue;
+             if (!(flags & SELECTION_SOURCE_ONLY))
+               isextra = 1;
              if (pool_disabled_solvable(pool, s))
+               {
+                 if (!(flags & SELECTION_WITH_DISABLED))
+                   continue;
+                 isextra = 1;
+               }
+           }
+         else
+           {
+             if ((flags & SELECTION_SOURCE_ONLY) != 0)
+               continue;
+             if (s->repo != pool->installed)
+               {
+                 if (pool_disabled_solvable(pool, s))
+                   {
+                     if (!(flags & SELECTION_WITH_DISABLED))
+                       continue;
+                     isextra = 1;
+                   }
+                 if (pool_badarch_solvable(pool, s))
+                   {
+                     if (!(flags & SELECTION_WITH_BADARCH))
+                       continue;
+                     isextra = 1;
+                   }
+               }
+           }
+         if (doprovides)
+           {
+             if (!isextra)
+               continue;       /* already done above in FOR_PROVIDES */
+             if (!s->provides || !matchprovides(pool, s, dep))
                continue;
-             havesrc = 1;
            }
-         else if (s->repo != pool->installed && !pool_installable(pool, s))
-           continue;
+         haveextra |= isextra;
          queue_push(&q, p);
        }
-      if (!havesrc || !q.count)
+      if (!haveextra || !q.count)
        continue;
       if (q.count == 1)
        {
-         selection->elements[i] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
+         selection->elements[i] = (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
          selection->elements[i + 1] = q.elements[0];
        }
       else
        {
-         selection->elements[i] = SOLVER_SOLVABLE_ONE_OF;
+         if (doprovides)
+           solv_sort(q.elements, q.count, sizeof(Id), selection_solvables_sortcmp, NULL);
+         selection->elements[i] = (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF;
          selection->elements[i + 1] = pool_queuetowhatprovides(pool, &q);
        }
     }
@@ -360,173 +474,332 @@ queue_pushunique2(Queue *q, Id id1, Id id2)
   queue_push2(q, id1, id2);
 }
 
+
+/*****  provides matching  *****/
+
 static int
-selection_depglob_id(Pool *pool, Queue *selection, Id id, int flags)
+selection_addextra_provides(Pool *pool, Queue *selection, const char *name, int flags)
 {
-  Id p, pp;
+  Id p, id, *idp;
   int match = 0;
+  int doglob, nocase, globflags;
 
-  FOR_PROVIDES(p, pp, id)
+  if ((flags & SELECTION_INSTALLED_ONLY) != 0)
+    return 0;  /* neither disabled nor badarch nor src */
+
+  nocase = flags & SELECTION_NOCASE;
+  doglob = (flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0;
+  globflags = doglob && nocase ? FNM_CASEFOLD : 0;
+
+  FOR_POOL_SOLVABLES(p)
     {
+      const char *n;
       Solvable *s = pool->solvables + p;
-      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+      if (!s->provides)
+       continue;
+      if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)        /* no provides */
+       continue;
+      if (s->repo == pool->installed)
        continue;
-      match = 1;
-      if (s->name == id && (flags & SELECTION_NAME) != 0)
+      if (pool_disabled_solvable(pool, s))
        {
-         if ((flags & SELECTION_SOURCE_ONLY) != 0)
-           id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
-         queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
-         if ((flags & SELECTION_WITH_SOURCE) != 0)
-           selection_addsrc(pool, selection, flags);
-         return SELECTION_NAME;
+         if (!(flags & SELECTION_WITH_DISABLED))
+           continue;
+         if (!(flags & SELECTION_WITH_BADARCH) && pool_badarch_solvable(pool, s))
+           continue;
        }
-    }
-  if ((flags & (SELECTION_SOURCE_ONLY | SELECTION_WITH_SOURCE)) != 0 && (flags & SELECTION_NAME) != 0)
-    {
-      /* src rpms don't have provides, so we must check every solvable */
-      FOR_POOL_SOLVABLES(p)    /* slow path */
+      else if (pool_badarch_solvable(pool, s))
        {
-         Solvable *s = pool->solvables + p;
-         if (s->name == id && (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC))
+         if (!(flags & SELECTION_WITH_BADARCH))
+           continue;
+       }
+      else
+       continue;
+      /* here is an extra solvable we need to consider */
+      idp = s->repo->idarraydata + s->provides;
+      while ((id = *idp++) != 0)
+       {
+         while (ISRELDEP(id))
            {
-             if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
-               continue;       /* just in case... src rpms can't be installed */
-             if (pool_disabled_solvable(pool, s))
-               continue;
-             if ((flags & SELECTION_SOURCE_ONLY) != 0)
-               id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
-             queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
-             if ((flags & SELECTION_WITH_SOURCE) != 0)
-               selection_addsrc(pool, selection, flags);
-             return SELECTION_NAME;
+             Reldep *rd = GETRELDEP(pool, id);
+             id = rd->name;
+           }
+         if (pool->whatprovides[id] > 1)
+           continue;   /* we already did that one in the normal code path */
+         n = pool_id2str(pool, id);
+         if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0)
+           {
+             queue_pushunique2(selection, SOLVER_SOLVABLE_PROVIDES, id);
+             match = 1;
            }
        }
     }
-  if (match && (flags & SELECTION_PROVIDES) != 0)
+  return match;
+}
+
+/* this is the fast path of selection_provides: the id for the name
+ * is known and thus we can quickly check the existance of a
+ * package with that provides */
+static int
+selection_provides_id(Pool *pool, Queue *selection, Id id, int flags)
+{
+  Id p, pp;
+
+  FOR_PROVIDES(p, pp, id)
+    {
+      Solvable *s = pool->solvables + p;
+      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+       continue;
+      break;
+    }
+  if (p)
     {
       queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
       return SELECTION_PROVIDES;
     }
+
+  if ((flags & (SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) != 0)
+    {
+      queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
+      selection_addextra(pool, selection, flags);
+      if (selection->elements[0] == SOLVER_SOLVABLE_PROVIDES)
+       queue_empty(selection);
+      else
+       {
+          selection->elements[0] = SOLVER_SOLVABLE_PROVIDES;
+          selection->elements[1] = id;
+       }
+      return selection->count ? SELECTION_PROVIDES : 0;
+    }
+
   return 0;
 }
 
+/* add missing provides matchers to the selection */
+/* match the provides of a package */
+/* note that we only return raw SOLVER_SOLVABLE_PROVIDES jobs
+ * so that the selection can be modified later. */
 static int
-selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
+selection_provides(Pool *pool, Queue *selection, const char *name, int flags)
 {
   Id id, p, pp;
-  int match = 0;
-  int doglob = 0;
-  int nocase = 0;
-  int globflags = 0;
+  int match;
+  int doglob;
+  int nocase;
+  int globflags;
+  const char *n;
 
   if ((flags & SELECTION_SOURCE_ONLY) != 0)
-    {
-      flags &= ~SELECTION_PROVIDES;    /* sources don't provide anything */
-      flags &= ~SELECTION_WITH_SOURCE;
-    }
-
-  if (!(flags & (SELECTION_NAME|SELECTION_PROVIDES)))
-    return 0;
-
-  if ((flags & SELECTION_INSTALLED_ONLY) != 0 && !pool->installed)
-    return 0;
+    return 0;  /* sources do not have provides */
 
   nocase = flags & SELECTION_NOCASE;
-  if (!nocase && !(flags & SELECTION_SKIP_KIND))
+  if (!nocase)
     {
+      /* try the fast path first */
       id = pool_str2id(pool, name, 0);
       if (id)
        {
-         /* the id is know, do the fast id matching using the whatprovides lookup */
-         int ret = selection_depglob_id(pool, selection, id, flags);
+         /* the id is known, do the fast id matching */
+         int ret = selection_provides_id(pool, selection, id, flags);
          if (ret)
            return ret;
        }
     }
 
-  if ((flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0)
-    doglob = 1;
-
-  if (!nocase && !(flags & SELECTION_SKIP_KIND) && !doglob)
-    return 0;  /* all done above in depglob_id */
-
-  if (doglob && nocase)
-    globflags = FNM_CASEFOLD;
+  doglob = (flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0;
+  if (!nocase && !doglob)
+    {
+      /* all done above in selection_provides_id */
+      return 0;
+    }
 
-  if ((flags & SELECTION_NAME) != 0)
+  /* looks like a glob or nocase match. really hard work. */
+  match = 0;
+  globflags = doglob && nocase ? FNM_CASEFOLD : 0;
+  for (id = 1; id < pool->ss.nstrings; id++)
     {
-      /* looks like a name glob. hard work. */
-      FOR_POOL_SOLVABLES(p)
+      /* do we habe packages providing this id? */
+      if (!pool->whatprovides[id] || pool->whatprovides[id] == 1)
+       continue;
+      n = pool_id2str(pool, id);
+      if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0)
        {
-         Solvable *s = pool->solvables + p;
-         const char *n;
-         if (s->repo != pool->installed && !pool_installable(pool, s))
+         if ((flags & SELECTION_INSTALLED_ONLY) != 0)
            {
-             if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
-               continue;
-             if (pool_disabled_solvable(pool, s))
+             FOR_PROVIDES(p, pp, id)
+               if (pool->solvables[p].repo == pool->installed)
+                 break;
+             if (!p)
                continue;
            }
-         if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+         queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
+         match = 1;
+       }
+    }
+
+  if (flags & (SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED))
+    match |= selection_addextra_provides(pool, selection, name, flags);
+
+  return match ? SELECTION_PROVIDES : 0;
+}
+
+/*****  name matching  *****/
+
+/* this is the fast path of selection_name: the id for the name
+ * is known and thus we can quickly check the existance of a
+ * package with that name */
+static int
+selection_name_id(Pool *pool, Queue *selection, Id id, int flags)
+{
+  Id p, pp, matchid;
+
+  matchid = id;
+  if ((flags & SELECTION_SOURCE_ONLY) != 0)
+    {
+      /* sources cannot be installed */
+      if ((flags & SELECTION_INSTALLED_ONLY) != 0)
+       return 0;
+      /* add ARCH_SRC to match only sources */
+      matchid = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
+      flags &= ~SELECTION_WITH_SOURCE;
+    }
+
+  FOR_PROVIDES(p, pp, matchid)
+    {
+      Solvable *s = pool->solvables + p;
+      if (s->name != id)
+       continue;
+      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+       continue;
+      /* one match is all we need */
+      queue_push2(selection, SOLVER_SOLVABLE_NAME, matchid);
+      /* add the requested extra packages */
+      if ((flags & (SELECTION_WITH_SOURCE | SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) != 0)
+       selection_addextra(pool, selection, flags);
+      return SELECTION_NAME;
+    }
+
+  if ((flags & (SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) != 0)
+    {
+      queue_push2(selection, SOLVER_SOLVABLE_NAME, matchid);
+      selection_addextra(pool, selection, flags);
+      if (selection->elements[0] == SOLVER_SOLVABLE_NAME)
+       queue_empty(selection);
+      return selection->count ? SELECTION_NAME : 0;
+    }
+
+  if ((flags & SELECTION_WITH_SOURCE) != 0 && (flags & SELECTION_INSTALLED_ONLY) == 0)
+    {
+      /* WITH_SOURCE case, but we had no match. try SOURCE_ONLY instead */
+      matchid = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
+      FOR_PROVIDES(p, pp, matchid)
+       {
+         Solvable *s = pool->solvables + p;
+         if (s->name != id)
            continue;
-         id = s->name;
-         n = pool_id2str(pool, id);
-         if (flags & SELECTION_SKIP_KIND)
-           n = skipkind(n);
-         if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0)
-           {
-             if ((flags & SELECTION_SOURCE_ONLY) != 0)
-               id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
-             queue_pushunique2(selection, SOLVER_SOLVABLE_NAME, id);
-             match = 1;
-           }
+         queue_push2(selection, SOLVER_SOLVABLE_NAME, matchid);
+         return SELECTION_NAME;
        }
-      if (match)
+    }
+  return 0;
+}
+
+/* match the name of a package */
+/* note that for SELECTION_INSTALLED_ONLY the result is not trimmed */
+static int
+selection_name(Pool *pool, Queue *selection, const char *name, int flags)
+{
+  Id id, p;
+  int match;
+  int doglob, nocase;
+  int globflags;
+  const char *n;
+
+  if ((flags & SELECTION_SOURCE_ONLY) != 0)
+    flags &= ~SELECTION_WITH_SOURCE;
+
+  nocase = flags & SELECTION_NOCASE;
+  if (!nocase && !(flags & SELECTION_SKIP_KIND))
+    {
+      /* try the fast path first */
+      id = pool_str2id(pool, name, 0);
+      if (id)
        {
-         if ((flags & SELECTION_WITH_SOURCE) != 0)
-           selection_addsrc(pool, selection, flags);
-          return SELECTION_NAME;
+         int ret = selection_name_id(pool, selection, id, flags);
+         if (ret)
+           return ret;
        }
     }
 
-  if ((flags & SELECTION_PROVIDES))
+  doglob = (flags & SELECTION_GLOB) != 0 && strpbrk(name, "[*?") != 0;
+  if (!nocase && !(flags & SELECTION_SKIP_KIND) && !doglob)
+    return 0;  /* all done above in selection_name_id */
+
+  /* do a name match over all packages. hard work. */
+  match = 0;
+  globflags = doglob && nocase ? FNM_CASEFOLD : 0;
+  FOR_POOL_SOLVABLES(p)
     {
-      /* looks like a dep glob. really hard work. */
-      for (id = 1; id < pool->ss.nstrings; id++)
+      Solvable *s = pool->solvables + p;
+      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+       continue;
+      if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
        {
-         const char *n;
-         if (!pool->whatprovides[id] || pool->whatprovides[id] == 1)
+         if (!(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE))
            continue;
-         n = pool_id2str(pool, id);
-         if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0)
+         if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s))
+           continue;
+       }
+      else if (s->repo != pool->installed)
+       {
+         if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s))
+           continue;
+         if (!(flags & SELECTION_WITH_BADARCH) && pool_badarch_solvable(pool, s))
+           continue;
+       }
+      id = s->name;
+      n = pool_id2str(pool, id);
+      if (flags & SELECTION_SKIP_KIND)
+       n = skipkind(n);
+      if ((doglob ? fnmatch(name, n, globflags) : nocase ? strcasecmp(name, n) : strcmp(name, n)) == 0)
+       {
+         if ((flags & SELECTION_SOURCE_ONLY) != 0)
            {
-             if ((flags & SELECTION_INSTALLED_ONLY) != 0)
-               {
-                 FOR_PROVIDES(p, pp, id)
-                   if (pool->solvables[p].repo == pool->installed)
-                     break;
-                 if (!p)
-                   continue;
-               }
-             queue_push2(selection, SOLVER_SOLVABLE_PROVIDES, id);
-             match = 1;
+             if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+               continue;
+             id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
            }
+         queue_pushunique2(selection, SOLVER_SOLVABLE_NAME, id);
+         match = 1;
        }
-      if (match)
-        return SELECTION_PROVIDES;
+    }
+  if (match)
+    {
+      /* if there was a match widen the selector to include all extra packages */
+      if ((flags & (SELECTION_WITH_SOURCE | SELECTION_WITH_BADARCH | SELECTION_WITH_DISABLED)) != 0)
+       selection_addextra(pool, selection, flags);
+      return SELECTION_NAME;
     }
   return 0;
 }
 
+
+/*****  SELECTION_DOTARCH and SELECTION_REL handling *****/
+
+/* like selection_name, but check for a .arch suffix if the match did
+   not work and SELECTION_DOTARCH is used */
 static int
-selection_depglob_arch(Pool *pool, Queue *selection, const char *name, int flags)
+selection_name_arch(Pool *pool, Queue *selection, const char *name, int flags, int doprovides, int noprune)
 {
   int ret;
   const char *r;
   Id archid;
 
-  if ((ret = selection_depglob(pool, selection, name, flags)) != 0)
+  if (doprovides)
+    ret = selection_provides(pool, selection, name, flags);
+  else
+    ret = selection_name(pool, selection, name, flags);
+  if (ret)
     return ret;
   if (!(flags & SELECTION_DOTARCH))
     return 0;
@@ -537,77 +810,34 @@ selection_depglob_arch(Pool *pool, Queue *selection, const char *name, int flags
       rname[r - name] = 0;
       if (archid == ARCH_SRC || archid == ARCH_NOSRC)
        flags |= SELECTION_SOURCE_ONLY;
-      if ((ret = selection_depglob(pool, selection, rname, flags)) != 0)
+      if (doprovides)
+       ret = selection_provides(pool, selection, rname, flags);
+      else
+       ret = selection_name(pool, selection, rname, flags);
+      if (ret)
        {
-         selection_filter_rel(pool, selection, REL_ARCH, archid);
-         solv_free(rname);
-         return ret | SELECTION_DOTARCH;
+         selection_filter_rel_noprune(pool, selection, REL_ARCH, archid);
+         if (!noprune)
+           selection_prune(pool, selection);
        }
       solv_free(rname);
+      return ret && selection->count ? ret | SELECTION_DOTARCH : 0;
     }
   return 0;
 }
 
-static int
-selection_filelist(Pool *pool, Queue *selection, const char *name, int flags)
+static char *
+splitrel(char *rname, char *r, int *rflagsp)
 {
-  Dataiterator di;
-  Queue q;
-  int type;
-
-  /* all files in the file list start with a '/' */
-  if (*name != '/')
+  int nend = r - rname;
+  int rflags = 0;
+  if (nend && *r == '=' && r[-1] == '!')
     {
-      if (!(flags & SELECTION_GLOB))
-       return 0;
-      if (*name != '*' && *name != '[' && *name != '?')
-       return 0;
+      nend--;
+      r++;
+      rflags = REL_LT|REL_GT;
     }
-  type = !(flags & SELECTION_GLOB) || strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB;
-  if ((flags & SELECTION_NOCASE) != 0)
-    type |= SEARCH_NOCASE;
-  queue_init(&q);
-  dataiterator_init(&di, pool, flags & SELECTION_INSTALLED_ONLY ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
-  while (dataiterator_step(&di))
-    {
-      Solvable *s = pool->solvables + di.solvid;
-      if (!s->repo)
-       continue;
-      if (s->repo != pool->installed && !pool_installable(pool, s))
-       {
-         if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
-           continue;
-         if (pool_disabled_solvable(pool, s))
-           continue;
-       }
-      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
-       continue;
-      queue_push(&q, di.solvid);
-      dataiterator_skip_solvable(&di);
-    }
-  dataiterator_free(&di);
-  if (!q.count)
-    return 0;
-  if (q.count > 1)
-    queue_push2(selection, SOLVER_SOLVABLE_ONE_OF, pool_queuetowhatprovides(pool, &q));
-  else
-    queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]);
-  queue_free(&q);
-  return SELECTION_FILELIST;
-}
-
-static char *
-splitrel(char *rname, char *r, int *rflagsp)
-{
-  int nend = r - rname;
-  int rflags = 0;
-  if (nend && *r == '=' && r[-1] == '!')
-    {
-      nend--;
-      r++;
-      rflags = REL_LT|REL_GT;
-    }
-  for (; *r; r++)
+  for (; *r; r++)
     {
       if (*r == '<')
        rflags |= REL_LT;
@@ -622,43 +852,178 @@ splitrel(char *rname, char *r, int *rflagsp)
     r++;
   while (nend && (rname[nend - 1] == ' ' || rname[nend - 1] == '\t'))
     nend--;
-  if (!*rname || !*r)
+  if (nend <= 0 || !*r || !rflags)
     return 0;
   *rflagsp = rflags;
   rname[nend] = 0;
   return r;
 }
 
+/* match name/provides, support DOTARCH and REL modifiers
+ */
 static int
-selection_rel(Pool *pool, Queue *selection, const char *name, int flags)
+selection_name_arch_rel(Pool *pool, Queue *selection, const char *name, int flags, int doprovides)
 {
-  int ret, rflags = 0;
-  char *r, *rname;
+  int ret, rflags = 0, noprune;
+  char *r = 0, *rname = 0;
 
-  /* relation case, support:
-   * depglob rel
-   * depglob.arch rel
-   */
-  rname = solv_strdup(name);
-  if ((r = strpbrk(rname, "<=>")) != 0)
+  /* try to split off an relation part */
+  if ((flags & SELECTION_REL) != 0)
     {
-      if ((r = splitrel(rname, r, &rflags)) == 0)
+      if ((r = strpbrk(name, "<=>")) != 0)
        {
-         solv_free(rname);
-         return 0;
+         rname = solv_strdup(name);
+         r = rname + (r - name);
+         if ((r = splitrel(rname, r, &rflags)) == 0)
+           rname = solv_free(rname);
        }
     }
-  if ((ret = selection_depglob_arch(pool, selection, rname, flags)) != 0)
+
+  /* check if we need to call selection_addextra */
+  noprune = doprovides && (flags & (SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH));
+
+  if (!r)
     {
-      if (rflags)
-       selection_filter_rel(pool, selection, rflags, pool_str2id(pool, r, 1));
-      solv_free(rname);
-      return ret | SELECTION_REL;
+      /* could not split off relation */
+      ret = selection_name_arch(pool, selection, name, flags, doprovides, noprune);
+      if (ret && noprune)
+       {
+         selection_addextra(pool, selection, flags);
+         selection_prune(pool, selection);
+       }
+      return ret && selection->count ? ret : 0;
+    }
+
+  /* we could split of a relation. prune name and then filter rel */
+  ret = selection_name_arch(pool, selection, rname, flags, doprovides, noprune);
+  if (ret)
+    {
+      selection_filter_rel_noprune(pool, selection, rflags, pool_str2id(pool, r, 1));
+      if (noprune)
+        selection_addextra(pool, selection, flags);
+      selection_prune(pool, selection);
     }
   solv_free(rname);
-  return 0;
+  return ret && selection->count ? ret | SELECTION_REL : 0;
 }
 
+/*****  filelist matching  *****/
+
+static int
+selection_filelist_sortcmp(const void *ap, const void *bp, void *dp)
+{
+  Pool *pool = dp;
+  const Id *a = ap, *b = bp;
+  if (a[0] != b[0])
+    return strcmp(pool_id2str(pool, a[0]), pool_id2str(pool, b[0]));
+  return a[1] - b[1];
+}
+
+static int
+selection_filelist(Pool *pool, Queue *selection, const char *name, int flags)
+{
+  Dataiterator di;
+  Queue q;
+  Id id;
+  int type;
+  int i, j, lastid;
+
+  /* all files in the file list start with a '/' */
+  if (*name != '/')
+    {
+      if (!(flags & SELECTION_GLOB))
+       return 0;
+      if (*name != '*' && *name != '[' && *name != '?')
+       return 0;
+    }
+  type = !(flags & SELECTION_GLOB) || strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB;
+  if ((flags & SELECTION_NOCASE) != 0)
+    type |= SEARCH_NOCASE;
+  queue_init(&q);
+  dataiterator_init(&di, pool, flags & SELECTION_INSTALLED_ONLY ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
+  while (dataiterator_step(&di))
+    {
+      Solvable *s = pool->solvables + di.solvid;
+      if (!s->repo)
+       continue;
+      if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+       {
+         if (!(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE))
+           continue;
+         if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s))
+           continue;
+       }
+      else
+       {
+         if ((flags & SELECTION_SOURCE_ONLY) != 0)
+           continue;
+         if (s->repo != pool->installed)
+           {
+             if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s))
+               continue;
+             if (!(flags & SELECTION_WITH_BADARCH) && pool_badarch_solvable(pool, s))
+               continue;
+           }
+       }
+      if ((flags & SELECTION_FLAT) != 0)
+       {
+         /* don't bother with the complex stuff */
+          queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, di.solvid);
+         dataiterator_skip_solvable(&di);
+         continue;
+        }
+      id = pool_str2id(pool, di.kv.str, 1);
+      queue_push2(&q, id, di.solvid);
+    }
+  dataiterator_free(&di);
+  if ((flags & SELECTION_FLAT) != 0)
+    {
+      queue_free(&q);
+      return selection->count ? SELECTION_FILELIST : 0;
+    }
+  if (!q.count)
+    {
+      queue_free(&q);
+      return 0;
+    }
+  solv_sort(q.elements, q.count / 2, 2 * sizeof(Id), selection_filelist_sortcmp, pool);
+  lastid = 0;
+  queue_push2(&q, 0, 0);
+  for (i = j = 0; i < q.count; i += 2)
+    {
+      if (q.elements[i] != lastid)
+       {
+         if (j == 1)
+           queue_pushunique2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]);
+         else if (j > 1)
+           {
+             int k;
+             Id *idp;
+             /* check if we already have it */
+             for (k = 0; k < selection->count; k += 2)
+               {
+                 if (selection->elements[k] != SOLVER_SOLVABLE_ONE_OF)
+                   continue;
+                 idp = pool->whatprovidesdata + selection->elements[k + 1];
+                 if (!memcmp(idp, q.elements, j * sizeof(Id)) && !idp[j])
+                   break;
+               }
+             if (k == selection->count)
+               queue_push2(selection, SOLVER_SOLVABLE_ONE_OF, pool_ids2whatprovides(pool, q.elements, j));
+           }
+          lastid = q.elements[i];
+          j = 0;
+       }
+      if (!j || q.elements[j - 1] != q.elements[i])
+        q.elements[j++] = q.elements[i + 1];
+    }
+  queue_free(&q);
+  return SELECTION_FILELIST;
+}
+
+
+/*****  canon name matching  *****/
+
 #if defined(MULTI_SEMANTICS)
 # define EVRCMP_DEPCMP (pool->disttype == DISTTYPE_DEB ? EVRCMP_COMPARE : EVRCMP_MATCH_RELEASE)
 #elif defined(DEBIAN)
@@ -669,11 +1034,22 @@ selection_rel(Pool *pool, Queue *selection, const char *name, int flags)
 
 /* magic epoch promotion code, works only for SELECTION_NAME selections */
 static void
-selection_filter_evr(Pool *pool, Queue *selection, char *evr)
+selection_filter_evr(Pool *pool, Queue *selection, const char *evr)
 {
   int i, j;
   Queue q;
   Id qbuf[10];
+  const char *sp;
+
+  /* do we already have an epoch? */
+  for (sp = evr; *sp >= '0' && *sp <= '9'; sp++)
+    ;
+  if (*sp == ':' && sp != evr)
+    {
+      /* yes, just add the rel filter */
+      selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, evr, 1));
+      return;
+    }
 
   queue_init(&q);
   queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
@@ -690,7 +1066,6 @@ selection_filter_evr(Pool *pool, Queue *selection, char *evr)
        {
          Solvable *s = pool->solvables + p;
          const char *sevr = pool_id2str(pool, s->evr);
-         const char *sp;
          for (sp = sevr; *sp >= '0' && *sp <= '9'; sp++)
            ;
          if (*sp != ':')
@@ -772,7 +1147,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
       rname = solv_strdup(name);       /* so we can modify it */
       r = rname + (r - name);
       *r++ = 0;
-      if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
+      if ((ret = selection_name(pool, selection, rname, flags)) == 0)
        {
          solv_free(rname);
          return 0;
@@ -785,7 +1160,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
        }
       selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1));
       solv_free(rname);
-      return ret | SELECTION_CANON;
+      return selection->count ? ret | SELECTION_CANON : 0;
     }
 
   if (pool->disttype == DISTTYPE_HAIKU)
@@ -795,7 +1170,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
       rname = solv_strdup(name);       /* so we can modify it */
       r = rname + (r - name);
       *r++ = 0;
-      if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
+      if ((ret = selection_name(pool, selection, rname, flags)) == 0)
        {
          solv_free(rname);
          return 0;
@@ -808,7 +1183,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
        }
       selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1));
       solv_free(rname);
-      return ret | SELECTION_CANON;
+      return selection->count ? ret | SELECTION_CANON : 0;
     }
 
   if ((r = strrchr(name, '-')) == 0)
@@ -824,7 +1199,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
     flags |= SELECTION_SOURCE_ONLY;
 
   /* try with just the version */
-  if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
+  if ((ret = selection_name(pool, selection, rname, flags)) == 0)
     {
       /* no luck, try with version-release */
       if ((r2 = strrchr(rname, '-')) == 0)
@@ -835,7 +1210,7 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
       *r = '-';
       *r2 = 0;
       r = r2;
-      if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
+      if ((ret = selection_name(pool, selection, rname, flags)) == 0)
        {
          solv_free(rname);
          return 0;
@@ -845,33 +1220,167 @@ selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
     selection_filter_rel(pool, selection, REL_ARCH, archid);
   selection_filter_evr(pool, selection, r + 1);        /* magic epoch promotion */
   solv_free(rname);
-  return ret | SELECTION_CANON;
+  return selection->count ? ret | SELECTION_CANON : 0;
+}
+
+/* return the needed withbits to match the provided selection */
+static int
+selection_extrabits(Pool *pool, Queue *selection, int flags)
+{
+  int i, needflags, isextra;
+  int allflags;
+  Id p;
+  Solvable *s;
+  Queue qlimit;
+
+  allflags = flags & (SELECTION_WITH_SOURCE | SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH);
+  if (!selection->count)
+    return allflags;
+  if (selection->count == 2 && selection->elements[0] == SOLVER_SOLVABLE_ALL)
+    return allflags;   /* don't bother */
+  queue_init(&qlimit);
+  selection_solvables(pool, selection, &qlimit);
+  needflags = 0;
+  for (i = 0; i < qlimit.count; i++)
+    {
+      p = qlimit.elements[i];
+      s = pool->solvables + p;
+      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
+       continue;
+      isextra = 0;
+      if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+       {
+         if (!(flags & SELECTION_WITH_SOURCE))
+           continue;
+          isextra |= SELECTION_WITH_SOURCE;
+         if (pool_disabled_solvable(pool, s))
+           {
+             if (!(flags & SELECTION_WITH_DISABLED))
+               continue;
+             isextra |= SELECTION_WITH_DISABLED;
+           }
+       }
+      else
+       {
+         if ((flags & SELECTION_SOURCE_ONLY) != 0)
+           continue;
+         if (s->repo != pool->installed)
+           {
+             if (pool_disabled_solvable(pool, s))
+               {
+                 if (!(flags & SELECTION_WITH_DISABLED))
+                   continue;
+                 isextra |= SELECTION_WITH_DISABLED;
+               }
+             if (pool_badarch_solvable(pool, s))
+               {
+                 if (!(flags & SELECTION_WITH_BADARCH))
+                   continue;
+                 isextra |= SELECTION_WITH_BADARCH;
+               }
+           }
+       }
+      if (isextra)
+       {
+         needflags |= isextra;
+         if (needflags == allflags)
+           break;
+       }
+    }
+  queue_free(&qlimit);
+  return needflags;
 }
 
 int
 selection_make(Pool *pool, Queue *selection, const char *name, int flags)
 {
   int ret = 0;
+  if ((flags & SELECTION_MODEBITS) != 0)
+    {
+      Queue q;
 
+      queue_init(&q);
+      if ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT || (flags & SELECTION_MODEBITS) == SELECTION_FILTER)
+       {
+         if (!selection->count)
+           {
+             queue_free(&q);
+             return 0;
+           }
+         if ((flags & (SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH | SELECTION_WITH_SOURCE)) != 0)
+           {
+             /* try to drop expensive extra bits */
+             flags = (flags & ~(SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH | SELECTION_WITH_SOURCE)) | selection_extrabits(pool, selection, flags);
+           }
+       }
+      ret = selection_make(pool, &q, name, flags & ~SELECTION_MODEBITS);
+      if ((flags & SELECTION_MODEBITS) == SELECTION_ADD)
+       selection_add(pool, selection, &q);
+      else if ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT)
+       selection_subtract(pool, selection, &q);
+      else if (ret || !(flags & SELECTION_FILTER_KEEP_IFEMPTY))
+       {
+         if ((flags & SELECTION_FILTER_SWAPPED) != 0)
+           {
+             selection_filter(pool, &q, selection);
+             queue_free(selection);
+             queue_init_clone(selection, &q);
+           }
+         else
+           selection_filter(pool, selection, &q);
+       }
+      queue_free(&q);
+      return ret;
+    }
   queue_empty(selection);
+  if ((flags & SELECTION_INSTALLED_ONLY) != 0 && !pool->installed)
+    return 0;
+
+  /* here come our four selection modes */
   if ((flags & SELECTION_FILELIST) != 0)
     ret = selection_filelist(pool, selection, name, flags);
-  if (!ret && (flags & SELECTION_REL) != 0 && strpbrk(name, "<=>") != 0)
-    ret = selection_rel(pool, selection, name, flags);
-  if (!ret)
-    ret = selection_depglob_arch(pool, selection, name, flags);
+  if (!ret && (flags & SELECTION_NAME) != 0)
+    ret = selection_name_arch_rel(pool, selection, name, flags, 0);
+  if (!ret && (flags & SELECTION_PROVIDES) != 0)
+    ret = selection_name_arch_rel(pool, selection, name, flags, 1);
   if (!ret && (flags & SELECTION_CANON) != 0)
     ret = selection_canon(pool, selection, name, flags);
-  if (selection->count && (flags & SELECTION_INSTALLED_ONLY) != 0)
-    selection_filter_installed(pool, selection);
-  if (ret && !selection->count)
-    ret = 0;   /* no match -> always return zero */
+
+  /* now do result filtering */
+  if (ret && (flags & SELECTION_INSTALLED_ONLY) != 0)
+    selection_filter_repo(pool, selection, pool->installed);
+
+  /* flatten if requested */
   if (ret && (flags & SELECTION_FLAT) != 0)
     selection_flatten(pool, selection);
-  return ret;
+  return selection->count ? ret : 0;
 }
 
-static inline int
+struct limiter {
+  int start;   /* either 2 or repofilter->start */
+  int end;     /* either nsolvables or repofilter->end */
+  Id *mapper;
+  Repo *repofilter;
+};
+
+/* add matching src packages to simple SOLVABLE_NAME selections */
+static void
+setup_limiter(Pool *pool, int flags, struct limiter *limiter)
+{
+  limiter->start = 2;
+  limiter->end = pool->nsolvables;
+  limiter->mapper = 0;
+  limiter->repofilter = 0;
+  if ((flags & SELECTION_INSTALLED_ONLY) != 0)
+    {
+      Repo *repo = pool->installed;
+      limiter->repofilter = repo;
+      limiter->start = repo ? repo->start : 0;
+      limiter->end = repo ? repo->end : 0;
+    }
+}
+
+static int
 matchdep_str(const char *pattern, const char *string, int flags)
 {
   if (flags & SELECTION_GLOB)
@@ -884,161 +1393,315 @@ matchdep_str(const char *pattern, const char *string, int flags)
   return strcmp(pattern, string) == 0 ? 1 : 0;
 }
 
+/* like pool_match_dep but uses matchdep_str to match the name for glob and nocase matching */
 static int
-matchdep(Pool *pool, Id id, char *rname, int rflags, char *revr, int flags)
+matchdep(Pool *pool, Id id, char *rname, int rflags, Id revr, int flags)
 {
   if (ISRELDEP(id))
     {
       Reldep *rd = GETRELDEP(pool, id);
-      if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_WITH || rd->flags == REL_WITHOUT || rd->flags == REL_COND || rd->flags == REL_UNLESS)
+      if (rd->flags > 7)
        {
-         if (matchdep(pool, rd->name, rname, rflags, revr, flags))
-           return 1;
-         if ((rd->flags == REL_COND || rd->flags == REL_UNLESS) && ISRELDEP(rd->evr))
+         if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_WITH || rd->flags == REL_WITHOUT || rd->flags == REL_COND || rd->flags == REL_UNLESS)
            {
-             rd = GETRELDEP(pool, rd->evr);
-             if (rd->flags != REL_ELSE)
-               return 0;
+             if (matchdep(pool, rd->name, rname, rflags, revr, flags))
+               return 1;
+             if ((rd->flags == REL_COND || rd->flags == REL_UNLESS) && ISRELDEP(rd->evr))
+               {
+                 rd = GETRELDEP(pool, rd->evr);
+                 if (rd->flags != REL_ELSE)
+                   return 0;
+               }
+             if (rd->flags != REL_COND && rd->flags != REL_UNLESS && rd->flags != REL_WITHOUT && matchdep(pool, rd->evr, rname, rflags, revr, flags))
+               return 1;
+             return 0;
            }
-         if (rd->flags != REL_COND && rd->flags != REL_UNLESS && rd->flags != REL_WITHOUT && matchdep(pool, rd->evr, rname, rflags, revr, flags))
-           return 1;
-         return 0;
+         if (rd->flags == REL_ARCH)
+           return matchdep(pool, rd->name, rname, rflags, revr, flags);
        }
-      if (rd->flags == REL_ARCH)
-       return matchdep(pool, rd->name, rname, rflags, revr, flags);
       if (!matchdep(pool, rd->name, rname, rflags, revr, flags))
        return 0;
-      if (rflags)
-       {
-         /* XXX: need pool_match_flags_evr here */
-         if (!pool_match_dep(pool, pool_rel2id(pool, rd->name, pool_str2id(pool, revr, 1), rflags, 1), id))
-           return 0;
-       }
+      if (rflags && !pool_intersect_evrs(pool, rd->flags, rd->evr, rflags, revr))
+       return 0;
       return 1;
     }
   return matchdep_str(rname, pool_id2str(pool, id), flags);
 }
 
-/*
- *  select against the dependencies in keyname
- *  like SELECTION_REL and SELECTION_PROVIDES, but with the
- *  deps in keyname instead of provides.
- */
-int
-selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int flags, int keyname, int marker)
+static int
+selection_make_matchdeps_common_limited(Pool *pool, Queue *selection, const char *name, Id dep, int flags, int keyname, int marker, struct limiter *limiter)
 {
-  char *rname, *r = 0;
+  int li, i, j;
+  int ret = 0;
+  char *rname = 0, *r = 0;
   int rflags = 0;
+  Id revr = 0;
   Id p;
   Queue q;
 
   queue_empty(selection);
-  rname = solv_strdup(name);
-  if (!(flags & SELECTION_MATCH_DEPSTR))
+  if (!limiter->end)
+    return 0;
+  if (!name && !dep)
+    return 0;
+
+  if ((flags & SELECTION_MATCH_DEPSTR) != 0)
+    flags &= ~SELECTION_REL;
+
+  if (name)
     {
-      if ((r = strpbrk(rname, "<=>")) != 0)
+      rname = solv_strdup(name);
+      if ((flags & SELECTION_REL) != 0)
        {
-         if ((r = splitrel(rname, r, &rflags)) == 0)
+         if ((r = strpbrk(rname, "<=>")) != 0)
            {
-             solv_free(rname);
-             return 0;
+             if ((r = splitrel(rname, r, &rflags)) == 0)
+               {
+                 solv_free(rname);
+                 return 0;
+               }
            }
+         revr = pool_str2id(pool, r, 1);
+         ret |= SELECTION_REL;
+       }
+      if ((flags & SELECTION_GLOB) != 0 && !strpbrk(rname, "[*?") != 0)
+       flags &= ~SELECTION_GLOB;
+
+      if ((flags & SELECTION_GLOB) == 0 && (flags & SELECTION_NOCASE) == 0 && (flags & SELECTION_MATCH_DEPSTR) == 0)
+       {
+         /* we can use the faster selection_make_matchdepid */
+         dep = pool_str2id(pool, rname, 1);
+         if (rflags)
+           dep = pool_rel2id(pool, dep, revr, rflags, 1);
+         rname = solv_free(rname);
+         name = 0;
+       }
+    }
+  if (dep)
+    {
+      if (keyname == SOLVABLE_NAME && (flags & SELECTION_MATCH_DEPSTR) != 0)
+       {
+         Reldep *rd;
+         if (!ISRELDEP(dep))
+           return 0;
+         rd = GETRELDEP(pool, dep);
+         if (!rd->name || rd->flags != REL_EQ)
+           return 0;
+         dep = rd->name;
+         rflags = rd->flags;
+         revr = rd->evr;
        }
     }
-  if ((flags & SELECTION_GLOB) != 0 && !strpbrk(rname, "[*?") != 0)
-    flags &= ~SELECTION_GLOB;
 
   queue_init(&q);
-  FOR_POOL_SOLVABLES(p)
+  for (li = limiter->start; li < limiter->end; li++)
     {
-      Solvable *s =  pool->solvables + p;
-      int i;
-
-      if (s->repo != pool->installed && !pool_installable(pool, s))
+      Solvable *s;
+      p = limiter->mapper ? limiter->mapper[li] : li;
+      s = pool->solvables + p;
+      if (!s->repo || (limiter->repofilter && s->repo != limiter->repofilter))
+       continue;
+      if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
        {
-         if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
+         if (!(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE))
            continue;
-         if (pool_disabled_solvable(pool, s))
+         if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s))
            continue;
        }
-      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
-       continue;
-      if ((s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) && !(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE))
-       continue;
+      else
+        {
+         if ((flags & SELECTION_SOURCE_ONLY) != 0)
+           continue;
+         if (s->repo != pool->installed)
+           {
+             if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s))
+               continue;
+             if (!(flags & SELECTION_WITH_BADARCH) && pool_badarch_solvable(pool, s))
+               continue;
+           }
+       }
+      if (keyname == SOLVABLE_NAME)                    /* nevr match hack */
+       {
+         if (dep)
+           {
+             if ((flags & SELECTION_MATCH_DEPSTR) != 0)
+               {
+                 if (s->name != dep || s->evr != revr)
+                   continue;
+               }
+             else
+               {
+                 if (!pool_match_nevr(pool, s, dep))
+                   continue;
+               }
+           }
+         else
+           {
+             if ((flags & SELECTION_MATCH_DEPSTR) != 0)        /* mis-use */
+               {
+                 char *tmp = pool_tmpjoin(pool, pool_id2str(pool, s->name), " = ", pool_id2str(pool, s->evr));
+                 if (!matchdep_str(rname, tmp, flags))
+                   continue;
+               }
+             else
+               {
+                 if (!matchdep(pool, s->name, rname, rflags, revr, flags))
+                   continue;
+                 if (rflags && !pool_intersect_evrs(pool, rflags, revr, REL_EQ, s->evr))
+                   continue;
+               }
+           }
+         queue_push(selection, p);
+         continue;
+       }
       queue_empty(&q);
       repo_lookup_deparray(s->repo, p, keyname, &q, marker);
-      for (i = 0; i < q.count; i++)
+      if (!q.count)
+       continue;
+      if (dep)
+       {
+         if ((flags & SELECTION_MATCH_DEPSTR) != 0)    /* mis-use */
+           {
+             for (i = 0; i < q.count; i++)
+               if (q.elements[i] == dep)
+                 break;
+           }
+         else
+           {
+             for (i = 0; i < q.count; i++)
+               if (pool_match_dep(pool, q.elements[i], dep))
+                 break;
+           }
+       }
+      else
        {
-         Id id = q.elements[i];
          if ((flags & SELECTION_MATCH_DEPSTR) != 0)
            {
-             if (matchdep_str(rname, pool_dep2str(pool, id), flags))
-               break;
-             continue;
+             for (i = 0; i < q.count; i++)
+               if (matchdep_str(rname, pool_dep2str(pool, q.elements[i]), flags))
+                 break;
+           }
+         else
+           {
+             for (i = 0; i < q.count; i++)
+               if (matchdep(pool, q.elements[i], rname, rflags, revr, flags))
+                 break;
            }
-         if (matchdep(pool, id, rname, rflags, r, flags))
-           break;
        }
       if (i < q.count)
-       queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, p);
+       queue_push(selection, p);
     }
   queue_free(&q);
   solv_free(rname);
   if (!selection->count)
     return 0;
+
+  /* convert package list to selection */
+  j = selection->count;
+  queue_insertn(selection, 0, selection->count, 0);
+  for (i = 0; i < selection->count; )
+    {
+      selection->elements[i++] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
+      selection->elements[i++] = selection->elements[j++];
+    }
+
   if ((flags & SELECTION_FLAT) != 0)
     selection_flatten(pool, selection);
-  return SELECTION_PROVIDES;
+  return ret | (keyname == SOLVABLE_NAME ? SELECTION_NAME : SELECTION_PROVIDES);
 }
 
-int
-selection_make_matchdepid(Pool *pool, Queue *selection, Id dep, int flags, int keyname, int marker)
+static int
+selection_make_matchdeps_common(Pool *pool, Queue *selection, const char *name, Id dep, int flags, int keyname, int marker)
 {
-  Id p;
-  Queue q;
+  struct limiter limiter;
 
-  queue_empty(selection);
-  if (!dep)
-    return 0;
-  queue_init(&q);
-  FOR_POOL_SOLVABLES(p)
+  setup_limiter(pool, flags, &limiter);
+  if ((flags & SELECTION_MODEBITS) != SELECTION_REPLACE)
     {
-      Solvable *s =  pool->solvables + p;
-      int i;
-
-      if (s->repo != pool->installed && !pool_installable(pool, s))
+      int ret;
+      Queue q, qlimit;
+      queue_init(&q);
+      queue_init(&qlimit);
+      /* deal with special filter cases */
+      if ((flags & SELECTION_MODEBITS) == SELECTION_FILTER && selection->count == 2 && limiter.end)
        {
-         if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
-           continue;
-         if (pool_disabled_solvable(pool, s))
-           continue;
+         if ((selection->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL)
+           flags = (flags & ~SELECTION_MODEBITS) | SELECTION_REPLACE;
+         else if ((selection->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_REPO)
+           {
+             Repo *repo = pool_id2repo(pool, selection->elements[1]);
+             if (limiter.repofilter && repo != limiter.repofilter)
+               repo = 0;
+             limiter.repofilter = repo;
+             limiter.start = repo ? repo->start : 0;
+             limiter.end = repo ? repo->end : 0;
+             flags = (flags & ~SELECTION_MODEBITS) | SELECTION_REPLACE;
+           }
        }
-      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
-       continue;
-      if ((s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) && !(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE))
-       continue;
-      queue_empty(&q);
-      repo_lookup_deparray(s->repo, p, keyname, &q, marker);
-      for (i = 0; i < q.count; i++)
+      if (limiter.end && ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT || (flags & SELECTION_MODEBITS) == SELECTION_FILTER))
+       {
+         selection_solvables(pool, selection, &qlimit);
+         limiter.start = 0;
+         limiter.end = qlimit.count;
+         limiter.mapper = qlimit.elements;
+       }
+      ret = selection_make_matchdeps_common_limited(pool, &q, name, dep, flags & ~SELECTION_MODEBITS, keyname, marker, &limiter);
+      queue_free(&qlimit);
+      if ((flags & SELECTION_MODEBITS) == SELECTION_ADD)
+       selection_add(pool, selection, &q);
+      else if ((flags & SELECTION_MODEBITS) == SELECTION_SUBTRACT)
+       selection_subtract(pool, selection, &q);
+      else if ((flags & SELECTION_MODEBITS) == SELECTION_FILTER)
        {
-         if ((flags & SELECTION_MATCH_DEPSTR) != 0)    /* mis-use */
+          if (ret || !(flags & SELECTION_FILTER_KEEP_IFEMPTY))
            {
-             if (q.elements[i] == dep)
-               break;
-             continue;
+             if ((flags & SELECTION_FILTER_SWAPPED) != 0)
+               {
+                 selection_filter(pool, &q, selection);
+                 queue_free(selection);
+                 queue_init_clone(selection, &q);
+               }
+             else
+               selection_filter(pool, selection, &q);
            }
-         if (pool_match_dep(pool, q.elements[i], dep))
-           break;
        }
-      if (i < q.count)
-       queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, p);
+      else if (ret || !(flags & SELECTION_FILTER_KEEP_IFEMPTY))
+       {
+         /* special filter case from above */
+         int i;
+         Id f = selection->elements[0] & ~(SOLVER_SELECTMASK|SOLVER_NOAUTOSET);        /* job, jobflags, setflags */
+         queue_free(selection);
+         queue_init_clone(selection, &q);
+         for (i = 0; i < selection->count; i += 2)
+           selection->elements[i] = (selection->elements[i] & (SOLVER_SELECTMASK | SOLVER_SETMASK)) | f;
+       }
+      queue_free(&q);
+      return ret;
     }
-  queue_free(&q);
-  if (!selection->count)
-    return 0;
-  if ((flags & SELECTION_FLAT) != 0)
-    selection_flatten(pool, selection);
-  return SELECTION_PROVIDES;
+  return selection_make_matchdeps_common_limited(pool, selection, name, dep, flags, keyname, marker, &limiter);
+}
+
+/*
+ *  select against the dependencies in keyname
+ *  like SELECTION_PROVIDES, but with the deps in keyname instead of provides.
+ *  supported match modifiers:
+ *    SELECTION_REL
+ *    SELECTION_GLOB
+ *    SELECTION_NOCASE
+ */
+int
+selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int flags, int keyname, int marker)
+{
+  return selection_make_matchdeps_common(pool, selection, name, 0, flags, keyname, marker);
+}
+
+/*
+ *  select against the dependency id in keyname
+ */
+int
+selection_make_matchdepid(Pool *pool, Queue *selection, Id dep, int flags, int keyname, int marker)
+{
+  return selection_make_matchdeps_common(pool, selection, 0, dep, flags, keyname, marker);
 }
 
 static inline int
@@ -1064,10 +1727,81 @@ pool_is_kind(Pool *pool, Id name, Id kind)
     }
 }
 
-void
-selection_filter(Pool *pool, Queue *sel1, Queue *sel2)
+static void
+selection_filter_map(Pool *pool, Queue *sel, Map *m, int setflags)
 {
   int i, j, miss;
+  Queue q;
+  Id p, pp;
+
+  queue_init(&q);
+  for (i = j = 0; i < sel->count; i += 2)
+    {
+      Id select = sel->elements[i] & SOLVER_SELECTMASK;
+      queue_empty(&q);
+      miss = 0;
+      if (select == SOLVER_SOLVABLE_ALL)
+       {
+         FOR_POOL_SOLVABLES(p)
+           {
+             if (map_tst(m, p))
+               queue_push(&q, p);
+             else
+               miss = 1;
+           }
+       }
+      else if (select == SOLVER_SOLVABLE_REPO)
+       {
+         Solvable *s;
+         Repo *repo = pool_id2repo(pool, sel->elements[i + 1]);
+         if (repo)
+           {
+             FOR_REPO_SOLVABLES(repo, p, s)
+               {
+                 if (map_tst(m, p))
+                   queue_push(&q, p);
+                 else
+                   miss = 1;
+               }
+           }
+       }
+      else
+       {
+         FOR_JOB_SELECT(p, pp, select, sel->elements[i + 1])
+           {
+             if (map_tst(m, p))
+               queue_pushunique(&q, p);
+             else
+               miss = 1;
+           }
+       }
+      if (!q.count)
+       continue;
+      if (!miss)
+       {
+         sel->elements[j] = sel->elements[i] | setflags;
+         sel->elements[j + 1] = sel->elements[i + 1];
+       }
+      else if (q.count > 1)
+       {
+         sel->elements[j] = (sel->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF | setflags;
+         sel->elements[j + 1] = pool_queuetowhatprovides(pool, &q);
+       }
+      else
+       {
+         sel->elements[j] = (sel->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE | SOLVER_NOAUTOSET | setflags;
+         sel->elements[j + 1] = q.elements[0];
+       }
+      j += 2;
+    }
+  queue_truncate(sel, j);
+  queue_free(&q);
+}
+
+static void
+selection_filter_int(Pool *pool, Queue *sel1, Queue *sel2, int invert)
+{
+  int i, j;
   Id p, pp, q1filled = 0;
   Queue q1;
   Map m2;
@@ -1075,10 +1809,12 @@ selection_filter(Pool *pool, Queue *sel1, Queue *sel2)
 
   if (!sel1->count || !sel2->count)
     {
+      if (invert && !sel2->count)
+       return;
       queue_empty(sel1);
       return;
     }
-  if (sel1->count == 2 && (sel1->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL)
+  if (sel1->count == 2 && (sel1->elements[0] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL && !invert)
     {
       /* XXX: not 100% correct, but very useful */
       p = sel1->elements[0] & ~(SOLVER_SELECTMASK | SOLVER_SETMASK);   /* job & jobflags */
@@ -1088,6 +1824,8 @@ selection_filter(Pool *pool, Queue *sel1, Queue *sel2)
         sel1->elements[i] = (sel1->elements[i] & (SOLVER_SELECTMASK | SOLVER_SETMASK)) | p ;
       return;
     }
+
+  /* convert sel2 into a map */
   queue_init(&q1);
   map_init(&m2, pool->nsolvables);
   for (i = 0; i < sel2->count; i += 2)
@@ -1097,6 +1835,8 @@ selection_filter(Pool *pool, Queue *sel1, Queue *sel2)
        {
          queue_free(&q1);
          map_free(&m2);
+         if (invert)
+           queue_empty(sel1);
          return;
        }
       if (select == SOLVER_SOLVABLE_REPO)
@@ -1147,78 +1887,34 @@ selection_filter(Pool *pool, Queue *sel1, Queue *sel2)
            map_set(&m2, p);
        }
     }
-  if (sel2->count == 2)                /* XXX: AND all setmasks instead? */
-    setflags = sel2->elements[0] & SOLVER_SETMASK & ~SOLVER_NOAUTOSET;
-  for (i = j = 0; i < sel1->count; i += 2)
-    {
-      Id select = sel1->elements[i] & SOLVER_SELECTMASK;
-      queue_empty(&q1);
-      miss = 0;
-      if (select == SOLVER_SOLVABLE_ALL)
-       {
-         FOR_POOL_SOLVABLES(p)
-           {
-             if (map_tst(&m2, p))
-               queue_push(&q1, p);
-             else
-               miss = 1;
-           }
-       }
-      else if (select == SOLVER_SOLVABLE_REPO)
-       {
-         Solvable *s;
-         Repo *repo = pool_id2repo(pool, sel1->elements[i + 1]);
-         if (repo)
-           {
-             FOR_REPO_SOLVABLES(repo, p, s)
-               {
-                 if (map_tst(&m2, p))
-                   queue_push(&q1, p);
-                 else
-                   miss = 1;
-               }
-           }
-       }
-      else
-       {
-         FOR_JOB_SELECT(p, pp, select, sel1->elements[i + 1])
-           {
-             if (map_tst(&m2, p))
-               queue_pushunique(&q1, p);
-             else
-               miss = 1;
-           }
-       }
-      if (!q1.count)
-       continue;
-      if (!miss)
-       {
-         sel1->elements[j] = sel1->elements[i] | setflags;
-         sel1->elements[j + 1] = sel1->elements[i + 1];
-       }
-      else if (q1.count > 1)
-       {
-         sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF | setflags;
-         sel1->elements[j + 1] = pool_queuetowhatprovides(pool, &q1);
-       }
-      else
-       {
-         sel1->elements[j] = (sel1->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE | SOLVER_NOAUTOSET | setflags;
-         sel1->elements[j + 1] = q1.elements[0];
-       }
-      j += 2;
-    }
-  queue_truncate(sel1, j);
   queue_free(&q1);
+
+  /* now filter sel1 with the map */
+  if (invert)
+    map_invertall(&m2);
+  if (sel2->count == 2)
+    setflags = sel2->elements[0] & SOLVER_SETMASK & ~SOLVER_NOAUTOSET;
+  selection_filter_map(pool, sel1, &m2, setflags);
   map_free(&m2);
 }
 
 void
+selection_filter(Pool *pool, Queue *sel1, Queue *sel2)
+{
+  selection_filter_int(pool, sel1, sel2, 0);
+}
+
+void
 selection_add(Pool *pool, Queue *sel1, Queue *sel2)
 {
-  int i;
-  for (i = 0; i < sel2->count; i++)
-    queue_push(sel1, sel2->elements[i]);
+  if (sel2->count)
+    queue_insertn(sel1, sel1->count, sel2->count, sel2->elements);
+}
+
+void
+selection_subtract(Pool *pool, Queue *sel1, Queue *sel2)
+{
+  selection_filter_int(pool, sel1, sel2, 1);
 }
 
 const char *
index 0dd6150..9938c2f 100644 (file)
 extern "C" {
 #endif
 
+/* what to match */
 #define SELECTION_NAME                 (1 << 0)
 #define SELECTION_PROVIDES             (1 << 1)
 #define SELECTION_FILELIST             (1 << 2)
 #define SELECTION_CANON                        (1 << 3)
-#define SELECTION_DOTARCH              (1 << 4)
-#define SELECTION_REL                  (1 << 5)
 
-#define SELECTION_INSTALLED_ONLY       (1 << 8)
+/* match extensions */
+#define SELECTION_DOTARCH              (1 << 4)        /* allow ".arch" suffix */
+#define SELECTION_REL                  (1 << 5)        /* allow "<=> rel" suffix */
+
+/* string comparison modifiers */
 #define SELECTION_GLOB                 (1 << 9)
-#define SELECTION_FLAT                 (1 << 10)
 #define SELECTION_NOCASE               (1 << 11)
+
+/* extra flags */
+#define SELECTION_FLAT                 (1 << 10)       /* flatten the resulting selection */
+#define SELECTION_SKIP_KIND            (1 << 14)       /* remove kind: name prefix in SELECTION_NAME matches */
+#define SELECTION_MATCH_DEPSTR         (1 << 15)       /* match dep2str result */
+
+/* package selection */
+#define SELECTION_INSTALLED_ONLY       (1 << 8)
 #define SELECTION_SOURCE_ONLY          (1 << 12)
 #define SELECTION_WITH_SOURCE          (1 << 13)
-#define SELECTION_SKIP_KIND            (1 << 14)
-#define SELECTION_MATCH_DEPSTR         (1 << 15)
+#define SELECTION_WITH_DISABLED                (1 << 16)
+#define SELECTION_WITH_BADARCH         (1 << 17)
+#define SELECTION_WITH_ALL             (SELECTION_WITH_SOURCE | SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH)
+
+/* result operator */
+#define SELECTION_REPLACE              (0 << 28)
+#define SELECTION_ADD                  (1 << 28)
+#define SELECTION_SUBTRACT             (2 << 28)
+#define SELECTION_FILTER               (3 << 28)
+
+#define SELECTION_MODEBITS             (3 << 28)       /* internal */
+
+/* extra SELECTION_FILTER bits */
+#define SELECTION_FILTER_KEEP_IFEMPTY  (1 << 30)
+#define SELECTION_FILTER_SWAPPED       (1 << 31)
+
 
 extern int  selection_make(Pool *pool, Queue *selection, const char *name, int flags);
 extern int  selection_make_matchdeps(Pool *pool, Queue *selection, const char *name, int flags, int keyname, int marker);
@@ -41,6 +65,8 @@ extern int  selection_make_matchdepid(Pool *pool, Queue *selection, Id dep, int
 
 extern void selection_filter(Pool *pool, Queue *sel1, Queue *sel2);
 extern void selection_add(Pool *pool, Queue *sel1, Queue *sel2);
+extern void selection_subtract(Pool *pool, Queue *sel1, Queue *sel2);
+
 extern void selection_solvables(Pool *pool, Queue *selection, Queue *pkgs);
 
 extern const char *pool_selection2str(Pool *pool, Queue *selection, Id flagmask);
index 037b33d..6405f4a 100644 (file)
 #define RULES_BLOCK 63
 
 
-static Id
-autouninstall(Solver *solv, Id *problem)
-{
-  Pool *pool = solv->pool;
-  int i;
-  int lastfeature = 0, lastupdate = 0;
-  Id v;
-  Id extraflags = -1;
-  Map *m = 0;
-
-  if (!solv->allowuninstall && !solv->allowuninstall_all)
-    {
-      if (!solv->allowuninstallmap.size)
-       return 0;               /* why did we get called? */
-      m = &solv->allowuninstallmap;
-    }
-  for (i = 0; (v = problem[i]) != 0; i++)
-    {
-      if (v < 0)
-       extraflags &= solv->job.elements[-v - 1];
-      if (v >= solv->updaterules && v < solv->updaterules_end)
-       {
-         Rule *r;
-         if (m && !MAPTST(m, v - solv->updaterules))
-           continue;
-         /* check if identical to feature rule, we don't like that (except for orphans) */
-         r = solv->rules + solv->featurerules + (v - solv->updaterules);
-         if (!r->p)
-           {
-             /* update rule == feature rule */
-             if (v > lastfeature)
-               lastfeature = v;
-             /* prefer orphaned packages in dup mode */
-             if (solv->keep_orphans)
-               {
-                 r = solv->rules + v;
-                 if (!r->d && !r->w2 && r->p == (solv->installed->start + (v - solv->updaterules)))
-                   {
-                     lastfeature = v;
-                     lastupdate = 0;
-                     break;
-                   }
-               }
-             continue;
-           }
-         if (v > lastupdate)
-           lastupdate = v;
-       }
-    }
-  if (!lastupdate && !lastfeature)
-    return 0;
-  v = lastupdate ? lastupdate : lastfeature;
-  POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "allowuninstall disabling ");
-  solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + v);
-  solver_disableproblem(solv, v);
-  if (extraflags != -1 && (extraflags & SOLVER_CLEANDEPS) != 0 && solv->cleandepsmap.size)
-    {
-      /* add the package to the updatepkgs list, this will automatically turn
-       * on cleandeps mode */
-      Id p = solv->rules[v].p;
-      if (!solv->cleandeps_updatepkgs)
-       {
-         solv->cleandeps_updatepkgs = solv_calloc(1, sizeof(Queue));
-         queue_init(solv->cleandeps_updatepkgs);
-       }
-      if (p > 0)
-       {
-         int oldupdatepkgscnt = solv->cleandeps_updatepkgs->count;
-          queue_pushunique(solv->cleandeps_updatepkgs, p);
-         if (solv->cleandeps_updatepkgs->count != oldupdatepkgscnt)
-           solver_disablepolicyrules(solv);
-       }
-    }
-  return v;
-}
-
 /************************************************************************/
 
 /*
@@ -164,17 +88,18 @@ enabledisablelearntrules(Solver *solv)
  * If we find a conflict, disable rules and add them to problem queue.
  */
 
-static void
-makeruledecisions(Solver *solv)
+static int
+makeruledecisions(Solver *solv, int disablerules)
 {
   Pool *pool = solv->pool;
-  int i, ri, ii;
+  int i, ri, ii, ori;
   Rule *r, *rr;
   Id v, vv;
   int decisionstart;
   int record_proof = 1;
   int oldproblemcount;
   int havedisabled = 0;
+  int doautouninstall;
 
   /* The system solvable is always installed first */
   assert(solv->decisionq.count == 0);
@@ -222,7 +147,7 @@ makeruledecisions(Solver *solv)
          if (!solv->decisionmap[vv])          /* if not yet decided */
            {
              queue_push(&solv->decisionq, v);
-             queue_push(&solv->decisionq_why, r - solv->rules);
+             queue_push(&solv->decisionq_why, ri);
              solv->decisionmap[vv] = v > 0 ? 1 : -1;
              IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
                {
@@ -254,14 +179,13 @@ makeruledecisions(Solver *solv)
              /* conflict with a learnt rule */
              /* can happen when packages cannot be installed for multiple reasons. */
              /* we disable the learnt rule in this case */
-             /* (XXX: we should really call analyze_unsolvable_rule here!) */
+             /* (XXX: we should really do something like analyze_unsolvable_rule here!) */
              solver_disablerule(solv, r);
              continue;
            }
 
          POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "ANALYZE UNSOLVABLE ASSERTION ----------------------\n");
-         IF_POOLDEBUG (SOLV_DEBUG_UNSOLVABLE)
-           solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + ri);
+         assert(ri >= solv->pkgrules_end);     /* must not have a conflict in the pkg rules! */
 
          /*
           * find the decision which is the "opposite" of the rule
@@ -270,136 +194,94 @@ makeruledecisions(Solver *solv)
            if (solv->decisionq.elements[i] == -v)
              break;
          assert(i < solv->decisionq.count);         /* assert that we found it */
-         oldproblemcount = solv->problems.count;
-
-         /*
-          * conflict with system solvable ?
-          */
          if (v == -SYSTEMSOLVABLE)
+           ori = 0;
+         else
            {
-             if (record_proof)
-               {
-                 queue_push(&solv->problems, solv->learnt_pool.count);
-                 queue_push(&solv->learnt_pool, ri);
-                 queue_push(&solv->learnt_pool, 0);
-               }
-             else
-               queue_push(&solv->problems, 0);
-             POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with system solvable, disabling rule #%d\n", ri);
-             if  (ri >= solv->jobrules && ri < solv->jobrules_end)
-               v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1);
-             else
-               v = ri;
-             queue_push(&solv->problems, v);
-             queue_push(&solv->problems, 0);
-             if (v >= solv->featurerules && v < solv->updaterules_end)
-               {
-                 if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
-                   if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
-                     {
-                       solv->problems.count = oldproblemcount;
-                       havedisabled = 1;
-                       break;  /* start over */
-                     }
-               }
-             solver_disableproblem(solv, v);
-             havedisabled = 1;
-             break;    /* start over */
+             ori = solv->decisionq_why.elements[i];            /* the conflicting rule */
+             assert(ori > 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 ?
-          */
-         if (solv->decisionq_why.elements[i] < solv->pkgrules_end)
+           * record the problem
+           */
+         doautouninstall = 0;
+         oldproblemcount = solv->problems.count;
+         queue_push(&solv->problems, 0);                       /* start problem */
+         if (ori < solv->pkgrules_end)
            {
-             if (record_proof)
+             /* easy: conflict with system solvable or pkg rule */
+             assert(v > 0 || v == -SYSTEMSOLVABLE);
+             IF_POOLDEBUG (SOLV_DEBUG_UNSOLVABLE)
                {
-                 queue_push(&solv->problems, solv->learnt_pool.count);
-                 queue_push(&solv->learnt_pool, ri);
-                 queue_push(&solv->learnt_pool, solv->decisionq_why.elements[i]);
-                 queue_push(&solv->learnt_pool, 0);
+                 if (ori)
+                   POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with pkg rule, disabling rule #%d\n", ri);
+                 else
+                   POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with system solvable, disabling rule #%d\n", ri);
+                 solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + ri);
+                 if (ori)
+                   solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + ori);
                }
-             else
-               queue_push(&solv->problems, 0);
-             assert(v > 0 || v == -SYSTEMSOLVABLE);
-             POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with pkg rule, disabling rule #%d\n", ri);
-             if (ri >= solv->jobrules && ri < solv->jobrules_end)
-               v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1);
-             else
-               v = ri;
-             queue_push(&solv->problems, v);
-             queue_push(&solv->problems, 0);
-             if (v >= solv->featurerules && v < solv->updaterules_end)
+             solver_recordproblem(solv, ri);
+             if (ri >= solv->featurerules && ri < solv->updaterules_end)
+               doautouninstall = 1;
+           }
+         else
+           {
+             POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflicting update/job assertions over literal %d\n", vv);
+             /*
+              * push all of our rules (can only be feature or job rules)
+              * asserting this literal on the problem stack
+              */
+             for (i = solv->pkgrules_end, rr = solv->rules + i; i < solv->learntrules; i++, rr++)
                {
-                 if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
-                   if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
-                     {
-                       solv->problems.count = oldproblemcount;
-                       havedisabled = 1;
-                       break;  /* start over */
-                     }
+                 if (rr->d < 0                          /* disabled */
+                     || rr->w2)                         /*  or no assertion */
+                   continue;
+                 if (rr->p != vv                        /* not affecting the literal */
+                     && rr->p != -vv)
+                   continue;
+                 if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, i))     /* weak: silently ignore */
+                   continue;
+                   
+                 POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, " - disabling rule #%d\n", i);
+                 solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + i);
+                 solver_recordproblem(solv, i);
+                 if (i >= solv->featurerules && i < solv->updaterules_end)
+                   doautouninstall = 1;
                }
-             solver_disableproblem(solv, v);
-             havedisabled = 1;
-             break;    /* start over */
            }
+         queue_push(&solv->problems, 0);                       /* finish problem */
 
-         /*
-          * conflict with another job or update/feature rule
-          */
+         /* try autouninstall if requested */
+         if (doautouninstall)
+           {
+             if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+               if (solver_autouninstall(solv, oldproblemcount) != 0)
+                 {
+                   solv->problems.count = oldproblemcount;
+                   havedisabled = 1;
+                   break;      /* start over */
+                 }
+           }
 
-         /* record proof */
+         /* record the proof if requested */
          if (record_proof)
            {
-             queue_push(&solv->problems, solv->learnt_pool.count);
+             solv->problems.elements[oldproblemcount] = solv->learnt_pool.count;
              queue_push(&solv->learnt_pool, ri);
-             queue_push(&solv->learnt_pool, solv->decisionq_why.elements[i]);
+             if (ori)
+               queue_push(&solv->learnt_pool, ori);
              queue_push(&solv->learnt_pool, 0);
            }
-         else
-           queue_push(&solv->problems, 0);
 
-         POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflicting update/job assertions over literal %d\n", vv);
-
-         /*
-          * push all of our rules (can only be feature or job rules)
-          * asserting this literal on the problem stack
-          */
-         for (i = solv->featurerules, rr = solv->rules + i; i < solv->learntrules; i++, rr++)
+         if (!disablerules)
            {
-             if (rr->d < 0                          /* disabled */
-                 || rr->w2)                         /*  or no assertion */
-               continue;
-             if (rr->p != vv                        /* not affecting the literal */
-                 && rr->p != -vv)
-               continue;
-             if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, i))     /* weak: silently ignore */
-               continue;
-               
-             POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, " - disabling rule #%d\n", i);
-             solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + i);
-               
-             v = i;
-             if (i >= solv->jobrules && i < solv->jobrules_end)
-               v = -(solv->ruletojob.elements[i - solv->jobrules] + 1);
-             queue_push(&solv->problems, v);
+             POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "UNSOLVABLE\n");
+             return -1;
            }
-         queue_push(&solv->problems, 0);
-
-         if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
-           if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
-             {
-               solv->problems.count = oldproblemcount;
-               havedisabled = 1;
-               break;  /* start over */
-             }
-
-         for (i = oldproblemcount + 1; i < solv->problems.count - 1; i++)
-           solver_disableproblem(solv, solv->problems.elements[i]);
+         /* disable all problem rules */
+         solver_disableproblemset(solv, oldproblemcount);
          havedisabled = 1;
          break;        /* start over */
        }
@@ -444,21 +326,15 @@ makeruledecisions(Solver *solv)
            continue;
 
          POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "assertion conflict, but I am weak, disabling ");
-         solver_printrule(solv, SOLV_DEBUG_UNSOLVABLE, r);
-
-         if (ri >= solv->jobrules && ri < solv->jobrules_end)
-           v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1);
-         else
-           v = ri;
-         solver_disableproblem(solv, v);
-         if (v < 0)
-           solver_reenablepolicyrules(solv, -v);
+         solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, r);
+         solver_fixproblem(solv, ri);
          havedisabled = 1;
          break;        /* start over */
        }
       if (ii == solv->ruleassertions.count)
        break;  /* finished! */
     }
+  return 1;            /* the new level */
 }
 
 
@@ -1013,36 +889,9 @@ analyze_unsolvable_rule(Solver *solv, Rule *r, Queue *weakq, Map *rseen)
     }
   if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, why) && weakq)
     queue_push(weakq, why);
-  /* do not add pkg rules to problem */
-  if (why < solv->pkgrules_end)
-    return;
-  /* turn rule into problem */
-  if (why >= solv->jobrules && why < solv->jobrules_end)
-    why = -(solv->ruletojob.elements[why - solv->jobrules] + 1);
-  /* normalize dup/infarch rules */
-  if (why > solv->infarchrules && why < solv->infarchrules_end)
-    {
-      Id name = pool->solvables[-solv->rules[why].p].name;
-      while (why > solv->infarchrules && pool->solvables[-solv->rules[why - 1].p].name == name)
-       why--;
-    }
-  if (why > solv->duprules && why < solv->duprules_end)
-    {
-      Id name = pool->solvables[-solv->rules[why].p].name;
-      while (why > solv->duprules && pool->solvables[-solv->rules[why - 1].p].name == name)
-       why--;
-    }
-
-  /* return if problem already countains our rule */
-  if (solv->problems.count)
-    {
-      for (i = solv->problems.count - 1; i >= 0; i--)
-       if (solv->problems.elements[i] == 0)    /* end of last problem reached? */
-         break;
-       else if (solv->problems.elements[i] == why)
-         return;
-    }
-  queue_push(&solv->problems, why);
+  /* add non-pkg rules to problem and disable */
+  if (why >= solv->pkgrules_end)
+    solver_recordproblem(solv, why);
 }
 
 
@@ -1149,24 +998,18 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules)
          return 0;
        }
       queue_free(&weakq);
-      if (lastweak >= solv->jobrules && lastweak < solv->jobrules_end)
-       v = -(solv->ruletojob.elements[lastweak - solv->jobrules] + 1);
-      else
-        v = lastweak;
       POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "disabling ");
       solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + lastweak);
       if (lastweak >= solv->choicerules && lastweak < solv->choicerules_end)
        solver_disablechoicerules(solv, solv->rules + lastweak);
-      solver_disableproblem(solv, v);
-      if (v < 0)
-       solver_reenablepolicyrules(solv, -v);
+      solver_fixproblem(solv, lastweak);
       solver_reset(solv);
       return 0;
     }
   queue_free(&weakq);
 
   if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
-    if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
+    if (solver_autouninstall(solv, oldproblemcount) != 0)
       {
        solv->problems.count = oldproblemcount;
        solv->learnt_pool.count = oldlearntpoolcount;
@@ -1184,8 +1027,7 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules)
   /* + 2: index + trailing zero */
   if (disablerules && oldproblemcount + 2 < solv->problems.count)
     {
-      for (i = oldproblemcount + 1; i < solv->problems.count - 1; i++)
-        solver_disableproblem(solv, solv->problems.elements[i]);
+      solver_disableproblemset(solv, oldproblemcount);
       /* XXX: might want to enable all weak rules again */
       solver_reset(solv);
       return 0;
@@ -2672,13 +2514,9 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
        {
          if (level < 0)
            break;
-         makeruledecisions(solv);
-         level = 1;
-         if (!disablerules && solv->problems.count)
-           {
-             level = -1;
-             break;
-           }
+         level = makeruledecisions(solv, disablerules);
+         if (level < 0)
+           break;
          POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "initial propagate (propagate_index: %d;  size decisionq: %d)...\n", solv->propagate_index, solv->decisionq.count);
          if ((r = propagate(solv, level)) != 0)
            {
@@ -3494,7 +3332,6 @@ solver_solve(Solver *solv, Queue *job)
     deduceq2addedmap(solv, &addedmap);
   if (solv->nrules != initialnrules)
     solver_shrinkrules(solv, initialnrules);
-  solv->nrules = initialnrules;
   solv->lastpkgrule = 0;
   solv->pkgrules_end = 0;
 
@@ -3588,7 +3425,7 @@ solver_solve(Solver *solv, Queue *job)
                }
              break;
            case SOLVER_DROP_ORPHANED:
-             if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
+             if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && installed && what == installed->repoid))
                solv->droporphanedmap_all = 1;
              FOR_JOB_SELECT(p, pp, select, what)
                {
@@ -4021,18 +3858,7 @@ solver_solve(Solver *solv, Queue *job)
 
   /* now create infarch and dup rules */
   if (!solv->noinfarchcheck)
-    {
-      solver_addinfarchrules(solv, &addedmap);
-#if 0
-      if (pool->implicitobsoleteusescolors)
-       {
-         /* currently doesn't work well with infarch rules, so make
-           * them weak */
-         for (i = solv->infarchrules; i < solv->infarchrules_end; i++)
-           queue_push(&solv->weakruleq, i);
-       }
-#endif
-    }
+    solver_addinfarchrules(solv, &addedmap);
   else
     solv->infarchrules = solv->infarchrules_end = solv->nrules;
 
@@ -4049,7 +3875,7 @@ solver_solve(Solver *solv, Queue *job)
   if (solv->bestupdatemap_all || solv->bestupdatemap.size || hasbestinstalljob)
     solver_addbestrules(solv, hasbestinstalljob);
   else
-    solv->bestrules = solv->bestrules_end = solv->nrules;
+    solv->bestrules = solv->bestrules_end = solv->bestrules_up = solv->nrules;
 
   if (needduprules)
     solver_freedupmaps(solv);  /* no longer needed */
@@ -4079,7 +3905,7 @@ solver_solve(Solver *solv, Queue *job)
   map_free(&installcandidatemap);
   queue_free(&q);
 
-  POOL_DEBUG(SOLV_DEBUG_STATS, "%d pkg rules, 2 * %d update rules, %d job rules, %d infarch rules, %d dup rules, %d choice rules, %d best rules\n", solv->pkgrules_end - 1, solv->updaterules_end - solv->updaterules, solv->jobrules_end - solv->jobrules, solv->infarchrules_end - solv->infarchrules, solv->duprules_end - solv->duprules, solv->choicerules_end - solv->choicerules, solv->bestrules_end - solv->bestrules);
+  POOL_DEBUG(SOLV_DEBUG_STATS, "%d pkg rules, 2 * %d update rules, %d job rules, %d infarch rules, %d dup rules, %d choice rules, %d best rules, %d yumobs rules\n", solv->pkgrules_end - 1, solv->updaterules_end - solv->updaterules, solv->jobrules_end - solv->jobrules, solv->infarchrules_end - solv->infarchrules, solv->duprules_end - solv->duprules, solv->choicerules_end - solv->choicerules, solv->bestrules_end - solv->bestrules, solv->yumobsrules_end - solv->yumobsrules);
   POOL_DEBUG(SOLV_DEBUG_STATS, "overall rule memory used: %d K\n", solv->nrules * (int)sizeof(Rule) / 1024);
 
   /* create weak map */
@@ -4716,351 +4542,6 @@ pool_isemptyupdatejob(Pool *pool, Id how, Id what)
   return 1;
 }
 
-static int
-get_userinstalled_cmp(const void *ap, const void *bp, void *dp)
-{
-  return *(Id *)ap - *(Id *)bp;
-}
-
-static int
-get_userinstalled_cmp_names(const void *ap, const void *bp, void *dp)
-{
-  Pool *pool = dp;
-  return strcmp(pool_id2str(pool, *(Id *)ap), pool_id2str(pool, *(Id *)bp));
-}
-
-static int
-get_userinstalled_cmp_namearch(const void *ap, const void *bp, void *dp)
-{
-  Pool *pool = dp;
-  int r;
-  r = strcmp(pool_id2str(pool, ((Id *)ap)[0]), pool_id2str(pool, ((Id *)bp)[0]));
-  if (r)
-    return r;
-  return strcmp(pool_id2str(pool, ((Id *)ap)[1]), pool_id2str(pool, ((Id *)bp)[1]));
-}
-
-static void
-get_userinstalled_sort_uniq(Pool *pool, Queue *q, int flags)
-{
-  Id lastp = -1, lasta = -1;
-  int i, j;
-  if (q->count < ((flags & GET_USERINSTALLED_NAMEARCH) ? 4 : 2))
-    return;
-  if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
-    solv_sort(q->elements, q->count / 2, 2 * sizeof(Id), get_userinstalled_cmp_namearch, pool);
-  else if ((flags & GET_USERINSTALLED_NAMES) != 0)
-    solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp_names, pool);
-  else
-    solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp, 0);
-  if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
-    {
-      for (i = j = 0; i < q->count; i += 2)
-       if (q->elements[i] != lastp || q->elements[i + 1] != lasta)
-         {
-           q->elements[j++] = lastp = q->elements[i];
-           q->elements[j++] = lasta = q->elements[i + 1];
-         }
-    }
-  else
-    {
-      for (i = j = 0; i < q->count; i++)
-       if (q->elements[i] != lastp)
-         q->elements[j++] = lastp = q->elements[i];
-    }
-  queue_truncate(q, j);
-}
-
-static void
-namearch2solvables(Pool *pool, Queue *q, Queue *qout, int job)
-{
-  int i;
-  if (!pool->installed)
-    return;
-  for (i = 0; i < q->count; i += 2)
-    {
-      Id p, pp, name = q->elements[i], arch = q->elements[i + 1];
-      FOR_PROVIDES(p, pp, name)
-       {
-         Solvable *s = pool->solvables + p;
-         if (s->repo != pool->installed || s->name != name || (arch && s->arch != arch))
-           continue;
-         if (job)
-           queue_push(qout, job);
-         queue_push(qout, p);
-       }
-    }
-}
-
-void
-solver_get_userinstalled(Solver *solv, Queue *q, int flags)
-{
-  Pool *pool = solv->pool;
-  Id p, p2, pp;
-  Solvable *s;
-  Repo *installed = solv->installed;
-  int i, j;
-  Map userinstalled;
-  
-  map_init(&userinstalled, 0);
-  queue_empty(q);
-  /* first process jobs */
-  for (i = 0; i < solv->job.count; i += 2)
-    {
-      Id how = solv->job.elements[i];
-      Id what, select;
-      if (installed && (how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
-       {
-         if (!userinstalled.size)
-           map_grow(&userinstalled, installed->end - installed->start);
-         what = solv->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);
-         continue;
-       }
-      if ((how & SOLVER_JOBMASK) != SOLVER_INSTALL)
-       continue;
-      if ((how & SOLVER_NOTBYUSER) != 0)
-       continue;
-      what = solv->job.elements[i + 1];
-      select = how & SOLVER_SELECTMASK;
-      FOR_JOB_SELECT(p, pp, select, what)
-        if (solv->decisionmap[p] > 0)
-         {
-           queue_push(q, p);
-#ifdef ENABLE_LINKED_PKGS
-           if (has_package_link(pool, pool->solvables + p))
-             {
-               int j;
-               Queue lq;
-               queue_init(&lq);
-               find_package_link(pool, pool->solvables + p, 0, &lq, 0, 0);
-               for (j = 0; j < lq.count; j++)
-                 if (solv->decisionmap[lq.elements[j]] > 0)
-                   queue_push(q, lq.elements[j]);
-             }
-#endif
-         }
-    }
-  /* now process updates of userinstalled packages */
-  if (installed && userinstalled.size)
-    {
-      for (i = 1; i < solv->decisionq.count; i++)
-       {
-         p = solv->decisionq.elements[i];
-         if (p <= 0)
-           continue;
-         s = pool->solvables + p;
-         if (!s->repo)
-           continue;
-         if (s->repo == installed)
-           {
-             if (MAPTST(&userinstalled, p - installed->start))
-               queue_push(q, p);
-             continue;
-           }
-         /* new package, check if we replace a userinstalled one */
-         FOR_PROVIDES(p2, pp, s->name)
-           {
-             Solvable *ps = pool->solvables + p2;
-             if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start))
-               continue;
-             if (!pool->implicitobsoleteusesprovides && s->name != ps->name)
-               continue;
-             if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
-               continue;
-             queue_push(q, p);
-             break;
-           }
-         if (!p2 && s->repo != installed && s->obsoletes)
-           {
-             Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
-             while ((obs = *obsp++) != 0)
-               {
-                 FOR_PROVIDES(p2, pp, obs)
-                   {
-                     Solvable *ps = pool->solvables + p2;
-                     if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start))
-                       continue;
-                     if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
-                       continue;
-                     if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) 
-                       continue;
-                     queue_push(q, p); 
-                     break;
-                   }
-                 if (p2)
-                   break;
-               }
-           }
-       }
-    }
-  map_free(&userinstalled);
-
-  /* convert to desired output format */
-  if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
-    {
-      int qcount = q->count;
-      queue_insertn(q, 0, qcount, 0);
-      for (i = j = 0; i < qcount; i++)
-       {
-         s = pool->solvables + q->elements[i + qcount];
-         q->elements[j++] = s->name;
-         q->elements[j++] = s->arch;
-       }
-    }
-  else if ((flags & GET_USERINSTALLED_NAMES) != 0)
-    {
-      for (i = 0; i < q->count; i++)
-       {
-         s = pool->solvables + q->elements[i];
-         q->elements[i] = s->name;
-       }
-    }
-  /* sort and unify */
-  get_userinstalled_sort_uniq(pool, q, flags);
-
-  /* invert if asked for */
-  if ((flags & GET_USERINSTALLED_INVERTED) != 0)
-    {
-      /* first generate queue with all installed packages */
-      Queue invq;
-      queue_init(&invq);
-      for (i = 1; i < solv->decisionq.count; i++)
-       {
-         p = solv->decisionq.elements[i];
-         if (p <= 0)
-           continue;
-         s = pool->solvables + p;
-         if (!s->repo)
-           continue;
-         if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
-           queue_push2(&invq, s->name, s->arch);
-         else if ((flags & GET_USERINSTALLED_NAMES) != 0)
-           queue_push(&invq, s->name);
-         else
-           queue_push(&invq, p);
-       }
-      /* push q on invq, just in case... */
-      queue_insertn(&invq, invq.count, q->count, q->elements);
-      get_userinstalled_sort_uniq(pool, &invq, flags);
-      /* subtract queues (easy as they are sorted and invq is a superset of q) */
-      if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
-       {
-         if (q->count)
-           {
-             for (i = j = 0; i < invq.count; i += 2)
-               if (invq.elements[i] == q->elements[j] && invq.elements[i + 1] == q->elements[j + 1])
-                 {
-                   invq.elements[i] = invq.elements[i + 1] = 0;
-                   j += 2;
-                   if (j >= q->count)
-                     break;
-                 }
-             queue_empty(q);
-           }
-         for (i = 0; i < invq.count; i += 2)
-           if (invq.elements[i])
-             queue_push2(q, invq.elements[i], invq.elements[i + 1]);
-       }
-      else
-       {
-         if (q->count)
-           {
-             for (i = j = 0; i < invq.count; i++)
-               if (invq.elements[i] == q->elements[j])
-                 {
-                   invq.elements[i] = 0;
-                   if (++j >= q->count)
-                     break;
-                 }
-             queue_empty(q);
-           }
-         for (i = 0; i < invq.count; i++)
-           if (invq.elements[i])
-             queue_push(q, invq.elements[i]);
-       }
-      queue_free(&invq);
-    }
-}
-
-void
-pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags)
-{
-  int i;
-
-  if ((flags & GET_USERINSTALLED_INVERTED) != 0)
-    {
-      Queue invq;
-      Id p, lastid;
-      Solvable *s;
-      int bad;
-      if (!pool->installed)
-       return;
-      queue_init(&invq);
-      if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
-       flags &= ~GET_USERINSTALLED_NAMES;      /* just in case */
-      FOR_REPO_SOLVABLES(pool->installed, p, s)
-       queue_push(&invq, flags & GET_USERINSTALLED_NAMES ? s->name : p);
-      if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
-       {
-         /* for namearch we convert to packages */
-         namearch2solvables(pool, q, &invq, 0);
-         get_userinstalled_sort_uniq(pool, &invq, flags);
-         namearch2solvables(pool, q, &invq, 0);
-         flags = 0;
-       }
-      else
-       {
-         queue_insertn(&invq, invq.count, q->count, q->elements);
-         get_userinstalled_sort_uniq(pool, &invq, flags);
-         /* now the fun part, add q again, sort, and remove all dups */
-         queue_insertn(&invq, invq.count, q->count, q->elements);
-       }
-      if (invq.count > 1)
-       {
-         if ((flags & GET_USERINSTALLED_NAMES) != 0)
-           solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp_names, pool);
-         else
-           solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp, 0);
-       }
-      lastid = -1;
-      bad = 1;
-      for (i = 0; i < invq.count; i++)
-       {
-         if (invq.elements[i] == lastid)
-           {
-             bad = 1;
-             continue;
-           }
-         if (!bad)
-           queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid);
-         bad = 0;
-         lastid = invq.elements[i];
-       }
-      if (!bad)
-       queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid);
-      queue_free(&invq);
-    }
-  else
-    {
-      if (flags & GET_USERINSTALLED_NAMEARCH)
-       namearch2solvables(pool, q, job, SOLVER_USERINSTALLED | SOLVER_SOLVABLE);
-      else
-       {
-         for (i = 0; i < q->count; i++)
-           queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), q->elements[i]);
-       }
-    }
-}
-
 int
 solver_alternatives_count(Solver *solv)
 {
index 1b85fb3..ebb2232 100644 (file)
@@ -68,6 +68,7 @@ struct _Solver {
   Id duprules_end;
 
   Id bestrules;                                /* rules from SOLVER_FORCEBEST */
+  Id bestrules_up;                     /* update rule part starts here*/
   Id bestrules_end;
   Id *bestrules_pkg;
 
diff --git a/src/userinstalled.c b/src/userinstalled.c
new file mode 100644 (file)
index 0000000..0efcdd7
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2017, SUSE LLC.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/* Functions that help getting/setting userinstalled packages. */
+
+#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 "util.h"
+#include "poolarch.h"
+#include "linkedpkg.h"
+
+static int
+get_userinstalled_cmp(const void *ap, const void *bp, void *dp)
+{
+  return *(Id *)ap - *(Id *)bp;
+}
+
+static int
+get_userinstalled_cmp_names(const void *ap, const void *bp, void *dp)
+{
+  Pool *pool = dp;
+  return strcmp(pool_id2str(pool, *(Id *)ap), pool_id2str(pool, *(Id *)bp));
+}
+
+static int
+get_userinstalled_cmp_namearch(const void *ap, const void *bp, void *dp)
+{
+  Pool *pool = dp;
+  int r;
+  r = strcmp(pool_id2str(pool, ((Id *)ap)[0]), pool_id2str(pool, ((Id *)bp)[0]));
+  if (r)
+    return r;
+  return strcmp(pool_id2str(pool, ((Id *)ap)[1]), pool_id2str(pool, ((Id *)bp)[1]));
+}
+
+static void
+get_userinstalled_sort_uniq(Pool *pool, Queue *q, int flags)
+{
+  Id lastp = -1, lasta = -1;
+  int i, j;
+  if (q->count < ((flags & GET_USERINSTALLED_NAMEARCH) ? 4 : 2))
+    return;
+  if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+    solv_sort(q->elements, q->count / 2, 2 * sizeof(Id), get_userinstalled_cmp_namearch, pool);
+  else if ((flags & GET_USERINSTALLED_NAMES) != 0)
+    solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp_names, pool);
+  else
+    solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp, 0);
+  if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+    {
+      for (i = j = 0; i < q->count; i += 2)
+       if (q->elements[i] != lastp || q->elements[i + 1] != lasta)
+         {
+           q->elements[j++] = lastp = q->elements[i];
+           q->elements[j++] = lasta = q->elements[i + 1];
+         }
+    }
+  else
+    {
+      for (i = j = 0; i < q->count; i++)
+       if (q->elements[i] != lastp)
+         q->elements[j++] = lastp = q->elements[i];
+    }
+  queue_truncate(q, j);
+}
+
+static void
+namearch2solvables(Pool *pool, Queue *q, Queue *qout, int job)
+{
+  int i;
+  if (!pool->installed)
+    return;
+  for (i = 0; i < q->count; i += 2)
+    {
+      Id p, pp, name = q->elements[i], arch = q->elements[i + 1];
+      FOR_PROVIDES(p, pp, name)
+       {
+         Solvable *s = pool->solvables + p;
+         if (s->repo != pool->installed || s->name != name || (arch && s->arch != arch))
+           continue;
+         if (job)
+           queue_push(qout, job);
+         queue_push(qout, p);
+       }
+    }
+}
+
+void
+solver_get_userinstalled(Solver *solv, Queue *q, int flags)
+{
+  Pool *pool = solv->pool;
+  Id p, p2, pp;
+  Solvable *s;
+  Repo *installed = solv->installed;
+  int i, j;
+  Map userinstalled;
+  
+  map_init(&userinstalled, 0);
+  queue_empty(q);
+  /* first process jobs */
+  for (i = 0; i < solv->job.count; i += 2)
+    {
+      Id how = solv->job.elements[i];
+      Id what, select;
+      if (installed && (how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
+       {
+         if (!userinstalled.size)
+           map_grow(&userinstalled, installed->end - installed->start);
+         what = solv->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);
+         continue;
+       }
+      if ((how & SOLVER_JOBMASK) != SOLVER_INSTALL)
+       continue;
+      if ((how & SOLVER_NOTBYUSER) != 0)
+       continue;
+      what = solv->job.elements[i + 1];
+      select = how & SOLVER_SELECTMASK;
+      FOR_JOB_SELECT(p, pp, select, what)
+        if (solv->decisionmap[p] > 0)
+         {
+           queue_push(q, p);
+#ifdef ENABLE_LINKED_PKGS
+           if (has_package_link(pool, pool->solvables + p))
+             {
+               int j;
+               Queue lq;
+               queue_init(&lq);
+               find_package_link(pool, pool->solvables + p, 0, &lq, 0, 0);
+               for (j = 0; j < lq.count; j++)
+                 if (solv->decisionmap[lq.elements[j]] > 0)
+                   queue_push(q, lq.elements[j]);
+             }
+#endif
+         }
+    }
+  /* now process updates of userinstalled packages */
+  if (installed && userinstalled.size)
+    {
+      for (i = 1; i < solv->decisionq.count; i++)
+       {
+         p = solv->decisionq.elements[i];
+         if (p <= 0)
+           continue;
+         s = pool->solvables + p;
+         if (!s->repo)
+           continue;
+         if (s->repo == installed)
+           {
+             if (MAPTST(&userinstalled, p - installed->start))
+               queue_push(q, p);
+             continue;
+           }
+         /* new package, check if we replace a userinstalled one */
+         FOR_PROVIDES(p2, pp, s->name)
+           {
+             Solvable *ps = pool->solvables + p2;
+             if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start))
+               continue;
+             if (!pool->implicitobsoleteusesprovides && s->name != ps->name)
+               continue;
+             if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
+               continue;
+             queue_push(q, p);
+             break;
+           }
+         if (!p2 && s->repo != installed && s->obsoletes)
+           {
+             Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
+             while ((obs = *obsp++) != 0)
+               {
+                 FOR_PROVIDES(p2, pp, obs)
+                   {
+                     Solvable *ps = pool->solvables + p2;
+                     if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start))
+                       continue;
+                     if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
+                       continue;
+                     if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) 
+                       continue;
+                     queue_push(q, p); 
+                     break;
+                   }
+                 if (p2)
+                   break;
+               }
+           }
+       }
+    }
+  map_free(&userinstalled);
+
+  /* convert to desired output format */
+  if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+    {
+      int qcount = q->count;
+      queue_insertn(q, 0, qcount, 0);
+      for (i = j = 0; i < qcount; i++)
+       {
+         s = pool->solvables + q->elements[i + qcount];
+         q->elements[j++] = s->name;
+         q->elements[j++] = s->arch;
+       }
+    }
+  else if ((flags & GET_USERINSTALLED_NAMES) != 0)
+    {
+      for (i = 0; i < q->count; i++)
+       {
+         s = pool->solvables + q->elements[i];
+         q->elements[i] = s->name;
+       }
+    }
+  /* sort and unify */
+  get_userinstalled_sort_uniq(pool, q, flags);
+
+  /* invert if asked for */
+  if ((flags & GET_USERINSTALLED_INVERTED) != 0)
+    {
+      /* first generate queue with all installed packages */
+      Queue invq;
+      queue_init(&invq);
+      for (i = 1; i < solv->decisionq.count; i++)
+       {
+         p = solv->decisionq.elements[i];
+         if (p <= 0)
+           continue;
+         s = pool->solvables + p;
+         if (!s->repo)
+           continue;
+         if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+           queue_push2(&invq, s->name, s->arch);
+         else if ((flags & GET_USERINSTALLED_NAMES) != 0)
+           queue_push(&invq, s->name);
+         else
+           queue_push(&invq, p);
+       }
+      /* push q on invq, just in case... */
+      queue_insertn(&invq, invq.count, q->count, q->elements);
+      get_userinstalled_sort_uniq(pool, &invq, flags);
+      /* subtract queues (easy as they are sorted and invq is a superset of q) */
+      if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+       {
+         if (q->count)
+           {
+             for (i = j = 0; i < invq.count; i += 2)
+               if (invq.elements[i] == q->elements[j] && invq.elements[i + 1] == q->elements[j + 1])
+                 {
+                   invq.elements[i] = invq.elements[i + 1] = 0;
+                   j += 2;
+                   if (j >= q->count)
+                     break;
+                 }
+             queue_empty(q);
+           }
+         for (i = 0; i < invq.count; i += 2)
+           if (invq.elements[i])
+             queue_push2(q, invq.elements[i], invq.elements[i + 1]);
+       }
+      else
+       {
+         if (q->count)
+           {
+             for (i = j = 0; i < invq.count; i++)
+               if (invq.elements[i] == q->elements[j])
+                 {
+                   invq.elements[i] = 0;
+                   if (++j >= q->count)
+                     break;
+                 }
+             queue_empty(q);
+           }
+         for (i = 0; i < invq.count; i++)
+           if (invq.elements[i])
+             queue_push(q, invq.elements[i]);
+       }
+      queue_free(&invq);
+    }
+}
+
+void
+pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags)
+{
+  int i;
+
+  if ((flags & GET_USERINSTALLED_INVERTED) != 0)
+    {
+      Queue invq;
+      Id p, lastid;
+      Solvable *s;
+      int bad;
+      if (!pool->installed)
+       return;
+      queue_init(&invq);
+      if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+       flags &= ~GET_USERINSTALLED_NAMES;      /* just in case */
+      FOR_REPO_SOLVABLES(pool->installed, p, s)
+       queue_push(&invq, flags & GET_USERINSTALLED_NAMES ? s->name : p);
+      if ((flags & GET_USERINSTALLED_NAMEARCH) != 0)
+       {
+         /* for namearch we convert to packages */
+         namearch2solvables(pool, q, &invq, 0);
+         get_userinstalled_sort_uniq(pool, &invq, flags);
+         namearch2solvables(pool, q, &invq, 0);
+         flags = 0;
+       }
+      else
+       {
+         queue_insertn(&invq, invq.count, q->count, q->elements);
+         get_userinstalled_sort_uniq(pool, &invq, flags);
+         /* now the fun part, add q again, sort, and remove all dups */
+         queue_insertn(&invq, invq.count, q->count, q->elements);
+       }
+      if (invq.count > 1)
+       {
+         if ((flags & GET_USERINSTALLED_NAMES) != 0)
+           solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp_names, pool);
+         else
+           solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp, 0);
+       }
+      lastid = -1;
+      bad = 1;
+      for (i = 0; i < invq.count; i++)
+       {
+         if (invq.elements[i] == lastid)
+           {
+             bad = 1;
+             continue;
+           }
+         if (!bad)
+           queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid);
+         bad = 0;
+         lastid = invq.elements[i];
+       }
+      if (!bad)
+       queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid);
+      queue_free(&invq);
+    }
+  else
+    {
+      if (flags & GET_USERINSTALLED_NAMEARCH)
+       namearch2solvables(pool, q, job, SOLVER_USERINSTALLED | SOLVER_SOLVABLE);
+      else
+       {
+         for (i = 0; i < q->count; i++)
+           queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), q->elements[i]);
+       }
+    }
+}
+
index aaf2aa1..93165c8 100644 (file)
@@ -11,19 +11,15 @@ system i686 rpm system
 
 job install name A [forcebest]
 result transaction,problems <inline>
-#>erase D-1-1.noarch@system
-#>install A-3-1.noarch@available
 #>problem 1210fdfb info package D-1-1.noarch conflicts with A = 3-1 provided by A-3-1.noarch
 #>problem 1210fdfb solution 0d75a914 erase D-1-1.noarch@system
-#>problem 1210fdfb solution d85f7c4e deljob install name A [forcebest]
+#>problem 1210fdfb solution ee74e60f deljob install name A [forcebest]
 
 # currently bestobeypolicy is a noop for install jobs
 nextjob
 solverflags bestobeypolicy
 job install name A [forcebest]
 result transaction,problems <inline>
-#>erase D-1-1.noarch@system
-#>install A-3-1.noarch@available
 #>problem 1210fdfb info package D-1-1.noarch conflicts with A = 3-1 provided by A-3-1.noarch
 #>problem 1210fdfb solution 0d75a914 erase D-1-1.noarch@system
-#>problem 1210fdfb solution d85f7c4e deljob install name A [forcebest]
+#>problem 1210fdfb solution ee74e60f deljob install name A [forcebest]
diff --git a/test/testcases/selection/selection_canon_rpm.t b/test/testcases/selection/selection_canon_rpm.t
new file mode 100644 (file)
index 0000000..018db31
--- /dev/null
@@ -0,0 +1,53 @@
+repo available 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Pkg: A 2 1 noarch
+#>=Prv: A = 3.1
+#>=Pkg: A 2 2 i686
+#>=Pkg: A 1:1 1 i686
+#>=Pkg: A 2 2 badarch
+#>=Pkg: A 1:3 1 i686
+system i686 rpm
+
+disable pkg E-1-1.src@available
+disable pkg F-1-1.src@available
+
+job noop selection A-2 canon
+result jobs <inline>
+#>job noop name A = 2 [setev]
+
+nextjob
+job noop selection A-2-1 canon
+result jobs <inline>
+#>job noop name A = 2-1 [setevr]
+
+nextjob
+job noop selection A-3 canon
+result jobs <inline>
+#>job noop name A = 1:3 [setev]
+
+nextjob
+job noop selection A-3-1 canon
+result jobs <inline>
+#>job noop name A = 1:3-1 [setevr]
+
+nextjob
+job noop selection A-1 canon
+result jobs <inline>
+#>job noop oneof A-1-1.noarch@available A-1:1-1.i686@available
+
+nextjob
+job noop selection A-1-1 canon
+result jobs <inline>
+#>job noop oneof A-1-1.noarch@available A-1:1-1.i686@available
+
+
+nextjob
+job noop selection A-0:1-1 canon
+result jobs <inline>
+#>job noop name A = 0:1-1 [setevr]
+
+nextjob
+job noop selection A-1:1-1 canon
+result jobs <inline>
+#>job noop name A = 1:1-1 [setevr]
+
diff --git a/test/testcases/selection/selection_filelist.t b/test/testcases/selection/selection_filelist.t
new file mode 100644 (file)
index 0000000..2005643
--- /dev/null
@@ -0,0 +1,20 @@
+repo available 0 testtags <inline>
+#>=Pkg: bash 1 1 noarch
+#>=Fls: /usr/bin/bash
+#>=Fls: /usr/bin/bashbug
+#>=Pkg: bash 2 1 noarch
+#>=Fls: /usr/bin/bash
+#>=Fls: /usr/bin/bashbug
+#>=Pkg: coreutils 1 1 noarch
+#>=Fls: /usr/bin/basename
+system i686 rpm
+
+job noop selection /usr/bin/ba* filelist,glob
+result jobs <inline>
+#>job noop oneof bash-1-1.noarch@available bash-2-1.noarch@available
+#>job noop pkg coreutils-1-1.noarch@available [noautoset]
+
+nextjob
+job noop selection /usr/bin/ba* filelist,glob,flat
+result jobs <inline>
+#>job noop oneof bash-1-1.noarch@available bash-2-1.noarch@available coreutils-1-1.noarch@available
diff --git a/test/testcases/selection/selection_matchdeps.t b/test/testcases/selection/selection_matchdeps.t
new file mode 100644 (file)
index 0000000..cdf9ebc
--- /dev/null
@@ -0,0 +1,54 @@
+repo available 0 testtags <inline>
+#>=Pkg: A 2 1 noarch
+#>=Pkg: AP 3 1 noarch
+#>=Prv: A = 3.1
+#>=Pkg: A 2 2 i686
+#>=Req: BBB > 5
+#>=Pkg: B 1 1 src
+#>=Pkg: A 2 2 badarch
+system i686 rpm
+
+job noop selection_matchdeps solvable:name a = 2 rel,flat,nocase
+result jobs <inline>
+#>job noop oneof A-2-1.noarch@available A-2-2.i686@available
+
+nextjob
+job noop selection_matchdeps solvable:name a = 2-* flat,glob,nocase,depstr
+result jobs <inline>
+#>job noop oneof A-2-1.noarch@available A-2-2.i686@available
+
+nextjob
+job noop selection_matchdepid solvable:name A = 2-1 flat
+result jobs <inline>
+#>job noop pkg A-2-1.noarch@available [noautoset]
+
+nextjob
+job noop selection_matchdepid solvable:name A = 2-2 flat,depstr
+result jobs <inline>
+#>job noop pkg A-2-2.i686@available [noautoset]
+
+nextjob
+job noop selection_matchdepid solvable:name A = 2-2 flat,depstr,withbadarch
+result jobs <inline>
+#>job noop oneof A-2-2.i686@available A-2-2.badarch@available
+
+nextjob
+job noop selection_matchdeps solvable:requires bbb < 10 rel,flat,nocase
+result jobs <inline>
+#>job noop pkg A-2-2.i686@available [noautoset]
+
+nextjob
+job noop selection_matchdeps solvable:requires bbb >* depstr,glob,flat,nocase
+result jobs <inline>
+#>job noop pkg A-2-2.i686@available [noautoset]
+
+nextjob
+job noop selection_matchdepid solvable:requires BBB < 10 flat
+result jobs <inline>
+#>job noop pkg A-2-2.i686@available [noautoset]
+
+nextjob
+job noop selection_matchdepid solvable:requires BBB > 5 flat,depstr
+result jobs <inline>
+#>job noop pkg A-2-2.i686@available [noautoset]
+
diff --git a/test/testcases/selection/selection_name.t b/test/testcases/selection/selection_name.t
new file mode 100644 (file)
index 0000000..b0a8520
--- /dev/null
@@ -0,0 +1,85 @@
+repo available 0 testtags <inline>
+#>=Pkg: A 2 1 noarch
+#>=Pkg: AP 3 1 noarch
+#>=Prv: A = 3.1
+#>=Pkg: A 2 2 i686
+#>=Req: BBB > 5
+#>=Pkg: B 1 1 src
+#>=Pkg: A 2 2 badarch
+#>=Pkg: C 2 2 badarch
+#>=Pkg: D 2 2 noarch
+#>=Pkg: D 2 2 badarch
+#>=Pkg: E 1 1 src
+#>=Pkg: F 1 1 src
+#>=Pkg: F 1 2 src
+#>=Pkg: G 1 1 src
+#>=Pkg: G 1 2 src
+system i686 rpm
+
+disable pkg E-1-1.src@available
+disable pkg F-1-1.src@available
+
+job noop selection A name
+result jobs <inline>
+#>job noop name A
+
+nextjob
+job noop selection A.i686 name,dotarch
+result jobs <inline>
+#>job noop name A . i686 [setarch]
+
+nextjob
+job noop selection A.i686>1 name,dotarch,rel
+result jobs <inline>
+#>job noop name (A . i686) > 1 [setarch]
+
+nextjob
+job noop selection B* glob,name,withsource
+result jobs <inline>
+#>job noop pkg B-1-1.src@available [noautoset]
+
+nextjob
+job noop selection A=2-2 name,dotarch,rel,withbadarch
+result jobs <inline>
+#>job noop oneof A-2-2.i686@available A-2-2.badarch@available [setevr]
+
+nextjob
+job noop selection C name,withbadarch
+result jobs <inline>
+#>job noop pkg C-2-2.badarch@available [noautoset]
+
+nextjob
+job noop selection D name,withbadarch
+result jobs <inline>
+#>job noop oneof D-2-2.noarch@available D-2-2.badarch@available
+
+nextjob
+job noop selection E name,sourceonly,withdisabled
+result jobs <inline>
+#>job noop pkg E-1-1.src@available [noautoset]
+
+nextjob
+job noop selection E name,withsource,withdisabled
+result jobs <inline>
+#>job noop pkg E-1-1.src@available [noautoset]
+
+nextjob
+job noop selection F name,sourceonly,withdisabled
+result jobs <inline>
+#>job noop oneof F-1-1.src@available F-1-2.src@available
+
+nextjob
+job noop selection F name,withsource,withdisabled
+result jobs <inline>
+#>job noop oneof F-1-1.src@available F-1-2.src@available
+
+nextjob
+job noop selection G name,sourceonly,withdisabled
+result jobs <inline>
+#>job noop name G . src
+
+nextjob
+job noop selection G name,withsource,withdisabled
+result jobs <inline>
+#>job noop oneof G-1-1.src@available G-1-2.src@available
+
diff --git a/test/testcases/selection/selection_provides.t b/test/testcases/selection/selection_provides.t
new file mode 100644 (file)
index 0000000..cb3a029
--- /dev/null
@@ -0,0 +1,49 @@
+repo available 0 testtags <inline>
+#>=Pkg: A 2 1 noarch
+#>=Pkg: AP 3 1 noarch
+#>=Prv: A = 3.1
+#>=Pkg: A 2 2 i686
+#>=Req: BBB > 5
+#>=Pkg: B 1 1 src
+#>=Pkg: A 2 2 badarch
+#>=Pkg: C 2 2 badarch
+#>=Pkg: D 2 2 badarch
+#>=Pkg: D 2 2 noarch
+system i686 rpm
+
+job noop selection A provides
+result jobs <inline>
+#>job noop provides A
+
+nextjob
+job noop selection A.i686 provides,dotarch
+result jobs <inline>
+#>job noop provides A . i686 [setarch]
+
+nextjob
+job noop selection A.i686>1 provides,dotarch,rel
+result jobs <inline>
+#>job noop provides (A . i686) > 1 [setarch]
+
+nextjob
+job noop selection A* glob,provides,withbadarch
+result jobs <inline>
+#>job noop oneof A-2-1.noarch@available AP-3-1.noarch@available A-2-2.i686@available A-2-2.badarch@available
+#>job noop provides AP
+
+nextjob
+job noop selection A*>=2 glob,provides,dotarch,rel,withbadarch
+result jobs <inline>
+#>job noop oneof A-2-1.noarch@available AP-3-1.noarch@available A-2-2.i686@available A-2-2.badarch@available
+#>job noop provides AP >= 2
+
+nextjob
+job noop selection C provides,withbadarch
+result jobs <inline>
+#>job noop pkg C-2-2.badarch@available [noautoset]
+
+
+nextjob
+job noop selection D provides,withbadarch
+result jobs <inline>
+#>job noop oneof D-2-2.badarch@available D-2-2.noarch@available
index e66b340..3331fae 100644 (file)
@@ -21,6 +21,9 @@ static struct resultflags2str {
   { TESTCASE_RESULT_ALTERNATIVES,       "alternatives" },
   { TESTCASE_RESULT_RULES,              "rules" },
   { TESTCASE_RESULT_GENID,              "genid" },
+  { TESTCASE_RESULT_REASON,             "reason" },
+  { TESTCASE_RESULT_CLEANDEPS,          "cleandeps" },
+  { TESTCASE_RESULT_JOBS,               "jobs" },
   { 0, 0 }
 };