current state of 'sat-solver'
authorKlaus Kaempf <kkaempf@suse.de>
Tue, 2 Oct 2007 12:40:07 +0000 (12:40 +0000)
committerKlaus Kaempf <kkaempf@suse.de>
Tue, 2 Oct 2007 12:40:07 +0000 (12:40 +0000)
49 files changed:
INSTALL [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
README [new file with mode: 0644]
bootstrap [new file with mode: 0755]
configure.in [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/bitmap.c [new file with mode: 0644]
src/bitmap.h [new file with mode: 0644]
src/evr.c [new file with mode: 0644]
src/evr.h [new file with mode: 0644]
src/hash.h [new file with mode: 0644]
src/pool.c [new file with mode: 0644]
src/pool.h [new file with mode: 0644]
src/poolarch.c [new file with mode: 0644]
src/poolarch.h [new file with mode: 0644]
src/poolid.c [new file with mode: 0644]
src/poolid.h [new file with mode: 0644]
src/poolid_private.h [new file with mode: 0644]
src/pooltypes.h [new file with mode: 0644]
src/queue.c [new file with mode: 0644]
src/queue.h [new file with mode: 0644]
src/solvable.h [new file with mode: 0644]
src/solver.c [new file with mode: 0644]
src/solver.h [new file with mode: 0644]
src/source.c [new file with mode: 0644]
src/source.h [new file with mode: 0644]
src/source_solv.c [new file with mode: 0644]
src/source_solv.h [new file with mode: 0644]
src/util.c [new file with mode: 0644]
src/util.h [new file with mode: 0644]
tools/Makefile.am [new file with mode: 0644]
tools/dumpsolv.c [new file with mode: 0644]
tools/helix2solv.c [new file with mode: 0644]
tools/patchxml2solv.c [new file with mode: 0644]
tools/rpmdb2solv.c [new file with mode: 0644]
tools/rpmmd2solv.c [new file with mode: 0644]
tools/source_helix.c [new file with mode: 0644]
tools/source_helix.h [new file with mode: 0644]
tools/source_patchxml.c [new file with mode: 0644]
tools/source_patchxml.h [new file with mode: 0644]
tools/source_rpmdb.c [new file with mode: 0644]
tools/source_rpmdb.h [new file with mode: 0644]
tools/source_rpmmd.c [new file with mode: 0644]
tools/source_rpmmd.h [new file with mode: 0644]
tools/source_susetags.c [new file with mode: 0644]
tools/source_susetags.h [new file with mode: 0644]
tools/source_write.c [new file with mode: 0644]
tools/source_write.h [new file with mode: 0644]
tools/susetags2solv.c [new file with mode: 0644]

diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..277c77b
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,26 @@
+Compiling and installing the software
+
+
+** This is a research project, NOT a complete tool. **
+
+
+Requirements
+
+- C compiler
+- automake/autoconf/libtool
+- make
+- expat
+- db-4.3
+
+Steps to compile
+
+1. sh bootstrap
+2. ./configure
+3. make
+
+
+Steps to run the testsuite
+
+1. cd testsuite
+2a. ./runtest.rb --redcarpet data.libredcarpet
+2b. ./runtest.rb data.libzypp
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..4cf1e17
--- /dev/null
@@ -0,0 +1,8 @@
+SUBDIRS = src tools testsuite
+
+AUTOMAKE_OPTIONS = no-dist-gzip dist-bzip2
+
+package: dist
+
+etags: TAGS
+       find . -name "*.[chCH]" -print | etags -
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..65f8e28
--- /dev/null
+++ b/README
@@ -0,0 +1,26 @@
+SAT-Solver
+
+Using a Satisfyability Solver to compute package dependencies.
+
+See http://idea.opensuse.org/content/ideas/fast-installation-tool
+for the motivation.
+
+
+This code is based on two major, but independent, blocks
+
+1. Using a dictionary approach to store and retrieve package
+   and dependency information.
+   
+2. Using satisfyability, a well known and researched topic, for
+   computing package dependencies.
+
+
+Google for 'sat solver' to get links to the theory behind it.
+http://del.icio.us/kkaempf/solver gives a collection of bookmarks
+related to this topic.
+
+Some research papers are in doc/pdf.
+
+Everything else is below doc.
+
+Please subscribe to zypp-devel@opensuse.org for any questions.
diff --git a/bootstrap b/bootstrap
new file mode 100755 (executable)
index 0000000..731fa44
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,18 @@
+#!/bin/sh 
+
+UNAME=`uname`
+
+if [ "$UNAME" = "Darwin" ]; then
+libtoolize --copy --force --automake
+aclocal-1.9
+autoheader-2.60
+automake-1.9 --add-missing --copy --foreign
+autoconf-2.60
+
+else
+libtoolize --copy --force --automake
+aclocal
+autoheader
+automake --add-missing --copy --foreign
+autoconf
+fi
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..9896eae
--- /dev/null
@@ -0,0 +1,137 @@
+dnl *******************************************
+dnl *** Initialize automake and set version ***
+dnl *******************************************
+
+AC_PREREQ(2.53)
+AC_INIT(satsolver, 0.0.1)
+AC_CONFIG_SRCDIR(src/solver.c)
+AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
+
+AM_CONFIG_HEADER(config.h)
+AM_MAINTAINER_MODE
+AC_PROG_MAKE_SET
+
+dnl ***************************
+dnl *** Set debugging flags ***
+dnl ***************************
+
+debug_default=minimum
+
+
+# Declare --enable-* args and collect ac_help strings
+AC_ARG_ENABLE(debug, 
+             [  --enable-debug=[no/minimum/yes] turn on debugging [default=$debug_default]],,
+             enable_debug=$debug_default)
+
+# Set the debug flags
+if test "x$enable_debug" = "xyes"; then
+    test "$cflags_set" = set || CFLAGS="$CFLAGS -g"
+fi
+
+# check for ssize_t
+AC_CHECK_TYPE(ssize_t, int)
+
+dnl ***************************
+dnl *** Checks for programs ***
+dnl ***************************
+
+
+AC_PROG_CC
+AM_PROG_CC_STDC
+AC_PROG_INSTALL
+
+# Set STDC_HEADERS
+AC_HEADER_STDC
+
+# Initialize libtool
+AM_PROG_LIBTOOL
+
+# This isn't a program, but it doesn't fit anywhere else...
+AC_FUNC_ALLOCA
+
+
+
+dnl ***********************
+dnl *** expat and db43  ***
+dnl ***********************
+
+AC_CHECK_LIB([expat], [XML_ParserCreate], [], [AC_MSG_ERROR(Please install expat)])
+
+AC_CHECK_LIB([db-4.3], [db_create], [], [AC_MSG_ERROR(Please install db43-devel)])
+
+dnl ***********************
+dnl *** Check for Win32 ***
+dnl ***********************
+
+AC_MSG_CHECKING([for Win32])
+case "$host" in
+  *-*-mingw*)
+    os_win32=yes
+    AC_CACHE_VAL(ac_cv_func_getaddrinfo, [ac_cv_func_getaddrinfo=yes])
+    AC_CACHE_VAL(ac_cv_func_getnameinfo, [ac_cv_func_getnameinfo=yes])
+    AC_CACHE_VAL(ac_cv_func_inet_pton, [ac_cv_func_inet_pton=yes])
+    AC_CACHE_VAL(ac_cv_func_inet_ntop, [ac_cv_func_inet_ntop=yes])
+    AC_CACHE_VAL(soup_cv_ipv6, [soup_cv_ipv6=yes])
+    ;;
+  *)
+    os_win32=no
+    ;;
+esac
+AC_MSG_RESULT([$os_win32])
+AM_CONDITIONAL(OS_WIN32, [test $os_win32 = yes])
+
+dnl *******************
+dnl *** Misc checks ***
+dnl *******************
+AC_CHECK_FUNCS(gmtime_r)
+dnl ----------------------------------------------------------------------
+AC_CHECK_HEADERS([inttypes.h stdlib.h])
+
+    AC_CHECK_SIZEOF(short)
+    AC_CHECK_SIZEOF(int)
+    AC_CHECK_SIZEOF(long)
+    AC_CHECK_SIZEOF(long long)
+    SIZEOF_SHORT=$ac_cv_sizeof_short
+    SIZEOF_INT=$ac_cv_sizeof_int
+    SIZEOF_LONG=$ac_cv_sizeof_long
+    SIZEOF_LONG_LONG=$ac_cv_sizeof_long_long
+    AC_SUBST(SIZEOF_SHORT)
+    AC_SUBST(SIZEOF_INT)
+    AC_SUBST(SIZEOF_LONG)
+    AC_SUBST(SIZEOF_LONG_LONG)
+
+if test "$prefix" = "NONE"; then
+    prefix=$ac_default_prefix;
+fi
+
+dnl *************************************
+dnl *** Warnings to show if using GCC ***
+dnl *************************************
+
+AC_ARG_ENABLE(more-warnings,
+             [  --disable-more-warnings  Inhibit compiler warnings],
+             set_more_warnings=no)
+
+if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
+       CFLAGS="$CFLAGS \
+               -Wall -Wstrict-prototypes -Wmissing-declarations \
+               -Wmissing-prototypes -Wnested-externs -Wpointer-arith \
+               -Wunused -Werror"
+fi
+
+if test "$os_win32" != yes; then
+       # Use reentrant functions (FIXME!)
+       CFLAGS="$CFLAGS -D_REENTRANT"
+fi
+
+dnl *************************
+dnl *** Output Everything ***
+dnl *************************
+AC_SUBST(SYSCONFDIR)
+
+AC_OUTPUT([
+       Makefile
+       src/Makefile
+       tools/Makefile
+       testsuite/Makefile
+       ])
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..ea3d4d4
--- /dev/null
@@ -0,0 +1,29 @@
+lib_LTLIBRARIES= libsatsolver.la
+solverincludedir = $(includedir)/satsolver
+
+solverinclude_HEADERS = \
+        pooltypes.h     \
+       hash.h          \
+       bitmap.h        \
+       evr.h           \
+       poolid.h        \
+       queue.h         \
+       solvable.h      \
+       source.h        \
+       pool.h          \
+       poolarch.h      \
+       solver.h        \
+       util.h          \
+       source_solv.h
+
+libsatsolver_la_SOURCES = \
+        bitmap.c        \
+       evr.c           \
+       poolid.c        \
+       queue.c         \
+       source.c        \
+       pool.c          \
+       poolarch.c      \
+       solver.c        \
+       util.c          \
+       source_solv.c
diff --git a/src/bitmap.c b/src/bitmap.c
new file mode 100644 (file)
index 0000000..e8ce806
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * bitmap.c
+ * 
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "bitmap.h"
+
+void
+mapinit(Map *m, int n)
+{
+  m->size = (n + 7) >> 3;
+  m->map = calloc(m->size, 1);
+}
+
+// free space allocated
+void
+mapfree(Map *m)
+{
+  free(m->map);
+  m->map = 0;
+  m->size = 0;
+}
+
+// copy t <- s
+void
+clonemap(Map *t, Map *s)
+{
+  t->size = s->size;
+  t->map = malloc(s->size);
+  memcpy(t->map, s->map, t->size);
+}
+
+// EOF
diff --git a/src/bitmap.h b/src/bitmap.h
new file mode 100644 (file)
index 0000000..5a73d24
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * bitmap.h
+ * 
+ */
+
+#ifndef BITMAP_H
+#define BITMAP_H
+
+typedef struct _Map {
+  unsigned char *map;
+  int size;
+} Map;
+
+#if 0
+#define MAPINIT(m, n) ((m)->size = ((n) + 7) >> 3, (m)->map = calloc((m)->size, 1))
+#endif
+
+#define MAPZERO(m) (memset((m)->map, 0, (m)->size))
+#define MAPSET(m, n) ((m)->map[(n) >> 3] |= 1 << ((n) & 7))
+#define MAPCLR(m, n) ((m)->map[(n) >> 3] &= ~(1 << ((n) & 7)))
+#define MAPTST(m, n) ((m)->map[(n) >> 3] & (1 << ((n) & 7)))
+
+extern void mapinit(Map *m, int n);
+extern void mapfree(Map *m);
+extern void clonemap(Map *t, Map *s);
+
+#endif /* BITMAP_H */
diff --git a/src/evr.c b/src/evr.c
new file mode 100644 (file)
index 0000000..588f24b
--- /dev/null
+++ b/src/evr.c
@@ -0,0 +1,138 @@
+/*
+ * evr.c
+ *
+ * version compare
+ */
+
+#include <string.h>
+#include "evr.h"
+#include "pool.h"
+
+int
+vercmp( const char *s1, const char *q1, const char *s2, const char *q2 )
+{
+  int r = 0;
+  const char *e1, *e2;
+
+  while (s1 < q1 && s2 < q2)
+    {
+      while (s1 < q1 && !(*s1 >= '0' && *s1 <= '9') &&
+          !(*s1 >= 'a' && *s1 <= 'z') && !(*s1 >= 'A' && *s1 <= 'Z'))
+       s1++;
+      while (s2 < q2 && !(*s2 >= '0' && *s2 <= '9') &&
+          !(*s2 >= 'a' && *s2 <= 'z') && !(*s2 >= 'A' && *s2 <= 'Z'))
+       s2++;
+      if ((*s1 >= '0' && *s1 <= '9') || (*s2 >= '0' && *s2 <= '9'))
+       {
+         while (*s1 == '0' && s1[1] >= '0' && s1[1] <= '9')
+           s1++;
+         while (*s2 == '0' && s2[1] >= '0' && s2[1] <= '9')
+           s2++;
+         for (e1 = s1; *e1 >= '0' && *e1 <= '9'; )
+           e1++;
+         for (e2 = s2; *e2 >= '0' && *e2 <= '9'; )
+           e2++;
+         r = e1 - s1 - (e2 - s2);
+          if (!r)
+           r = strncmp(s1, s2, e1 - s1);
+          if (r)
+           return r > 0 ? 1 : -1;
+       }
+      else
+       {
+         for (e1 = s1; (*e1 >= 'a' && *e1 <= 'z') || (*e1 >= 'A' && *e1 <= 'Z'); )
+           e1++;
+         for (e2 = s2; (*e2 >= 'a' && *e2 <= 'z') || (*e2 >= 'A' && *e2 <= 'Z'); )
+           e2++;
+         r = e1 - s1 - (e2 - s2);
+          if (r > 0)
+           {
+             r = strncmp(s1, s2, e2 - s2);
+             return r >= 0 ? 1 : -1;
+           }
+          if (r < 0)
+           {
+             r = strncmp(s1, s2, e1 - s1);
+             return r <= 0 ? -1 : 1;
+           }
+         r = strncmp(s1, s2, e1 - s1);
+         if (r)
+           return r > 0 ? 1 : -1;
+       }
+      s1 = e1;
+      s2 = e2;
+    }
+  return s1 < q1 ? 1 : s2 < q2 ? -1 : 0;
+}
+
+
+// edition (e:v.r) compare
+int
+evrcmp( Pool *pool, Id evr1id, Id evr2id )
+{
+  int r;
+  const char *evr1, *evr2;
+  const char *s1, *s2;
+  const char *r1, *r2;
+
+  if (evr1id == evr2id)
+    return 0;
+  evr1 = id2str( pool, evr1id );
+  evr2 = id2str( pool, evr2id );
+
+#if 0
+  printf("evrcmp %s %s\n", evr1, evr2);
+#endif
+  for (s1 = evr1; *s1 >= '0' && *s1 <= '9'; s1++)
+    ;
+  for (s2 = evr2; *s2 >= '0' && *s2 <= '9'; s2++)
+    ;
+  if (s1 == evr1 || *s1 != ':')
+    s1 = 0;
+  if (s2 == evr2 || *s2 != ':')
+    s2 = 0;
+  if (s1 && s2)
+    {
+      r = vercmp( evr1, s1, evr2, s2 );
+      if (r)
+       return r;
+      evr1 = s1 + 1;
+      evr2 = s2 + 1;
+    }
+  else if (s1)
+    {
+      if (!pool->promoteepoch)
+       {
+         while(*evr1 == '0')
+           evr1++;
+         if (*evr1 != ':')
+           return 1;
+       }
+      evr1 = s1 + 1;
+    }
+  else if (s2)
+    {
+      while(*evr2 == '0')
+       evr2++;
+      if (*evr2 != ':')
+       return -1;
+      evr2 = s1 + 1;
+    }
+  for (s1 = evr1, r1 = 0; *s1; s1++)
+    if (*s1 == '-')
+      r1 = s1;
+  for (s2 = evr2, r2 = 0; *s2; s2++)
+    if (*s2 == '-')
+      r2 = s2;
+  r = vercmp(evr1, r1 ? r1 : s1, evr2, r2 ? r2 : s2);
+  if (r)
+    return r;
+  if (r1 && r2)
+    {
+      if (s1 != ++r1 && s2 != ++r2)
+        r = vercmp(r1, s1, r2, s2);
+    }
+  return r;
+}
+
+// EOF
diff --git a/src/evr.h b/src/evr.h
new file mode 100644 (file)
index 0000000..ff8a5d8
--- /dev/null
+++ b/src/evr.h
@@ -0,0 +1,14 @@
+/*
+ * evr.h
+ * 
+ */
+
+#ifndef EVR_H
+#define EVR_H
+
+#include "pooltypes.h"
+
+extern int vercmp( const char *s1, const char *q1, const char *s2, const char *q2 );
+extern int evrcmp( Pool *pool, Id evr1id, Id evr2id );
+
+#endif /* EVR_H */
diff --git a/src/hash.h b/src/hash.h
new file mode 100644 (file)
index 0000000..f2534e9
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * hash.h
+ * generic hash functions
+ */
+
+#ifndef HASH_H
+#define HASH_H
+
+#include "pooltypes.h"
+
+/* value of a hash */
+typedef unsigned int Hashval;
+/* mask for hash, used as modulo operator to ensure 'wrapping' of hash
+   values -> hash table */
+typedef unsigned int Hashmask;
+
+/* inside the hash table, Ids are stored. Hash maps: string -> hash -> Id */
+typedef Id *Hashtable;
+
+/* hash chain */
+#define HASHCHAIN_START 7
+#define HASHCHAIN_NEXT(h, hh, mask) (((h) + (hh)++) & (mask))
+
+/* very simple hash function
+ * string -> hash
+ */
+static inline Hashval
+strhash(const char *str)
+{
+  Hashval r = 0;
+  unsigned int c;
+  while ((c = *(const unsigned char *)str++) != 0)
+    r += (r << 3) + c;
+  return r;
+}
+
+/* hash for rel
+ * rel -> hash
+ */
+static inline Hashval
+relhash(Id name, Id evr, int flags)
+{
+  return name + 7 * evr + 13 * flags;
+}
+
+
+/* compute bitmask for value
+ * returns smallest (2^n-1) > num
+ * 
+ * used for Hashtable 'modulo' operation
+ */ 
+static inline Hashmask
+mkmask(unsigned int num)
+{
+  num *= 2;
+  while (num & (num - 1))
+    num &= num - 1;
+  return num * 2 - 1;
+}
+
+#endif /* HASH_H */
diff --git a/src/pool.c b/src/pool.c
new file mode 100644 (file)
index 0000000..6a4a20f
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * pool.c
+ * 
+ * The pool contains information about solvables
+ * stored optimized for memory consumption and fast retrieval.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "pool.h"
+#include "poolid.h"
+#include "poolid_private.h"
+#include "poolarch.h"
+#include "util.h"
+#include "evr.h"
+
+
+// reset all whatprovides
+// 
+void
+pool_freewhatprovides(Pool *pool)
+{
+  pool->whatprovides = xfree(pool->whatprovides);
+  pool->whatprovidesdata = xfree(pool->whatprovidesdata);
+  pool->whatprovidesdataoff = 0;
+  pool->whatprovidesdataleft = 0;
+}
+
+
+// list of string constants, so we can do pointer/Id instead of string comparison
+// index into array matches ID_xxx constants in pool.h
+
+static char *initpool_data[] = {
+  "<NULL>",                   // ID_NULL
+  "",                         // ID_EMPTY
+  "solvable:name",
+  "solvable:arch",
+  "solvable:evr",
+  "solvable:provides",
+  "solvable:obsoletes",
+  "solvable:conflicts",
+  "solvable:requires",
+  "solvable:recommends",
+  "solvable:suggests",
+  "solvable:supplements",
+  "solvable:enhances",
+  "solvable:freshens",
+  "rpm:dbid",                         /* direct key into rpmdb */
+  "solvable:prereqmarker",
+  "solvable:filemarker",
+  "src",
+  "nosrc",
+  "noarch"
+};
+
+// create pool
+// 
+Pool *
+pool_create(void)
+{
+  int count, totalsize = 0;
+  Pool *pool;
+
+  pool = (Pool *)xcalloc(1, sizeof(*pool));
+
+  // count number and total size of predefined strings
+  for (count = 0; count < sizeof(initpool_data)/sizeof(*initpool_data); count++)
+    totalsize += strlen(initpool_data[count]) + 1;
+
+  // alloc appropriate space
+  pool->stringspace = (char *)xmalloc((totalsize + STRINGSPACE_BLOCK) & ~STRINGSPACE_BLOCK);
+  pool->strings = (Offset *)xmalloc(((count + STRING_BLOCK) & ~STRING_BLOCK) * sizeof(Offset));
+
+  // now copy predefined strings into allocated space
+  pool->sstrings = 0;
+  for (count = 0; count < sizeof(initpool_data)/sizeof(*initpool_data); count++)
+    {
+      strcpy(pool->stringspace + pool->sstrings, initpool_data[count]);
+      pool->strings[count] = pool->sstrings;
+      pool->sstrings += strlen(initpool_data[count]) + 1;
+    }
+  pool->nstrings = count;
+
+  // pre-alloc space for a RelDep
+  pool->rels = (Reldep *)xcalloc(1 + REL_BLOCK, sizeof(Reldep));
+  pool->nrels = 1;
+
+  // pre-alloc space for a Solvable
+  pool->solvables = (Solvable *)xcalloc(1, sizeof(Solvable));
+  pool->nsolvables = 1;
+
+  return pool;
+}
+
+
+// empty the pool
+// 
+void
+pool_free(Pool *pool)
+{
+  int i;
+  Source *source;
+
+  pool_freewhatprovides(pool);
+  pool_freeidhashes(pool);
+  for (i = 0; i < pool->nsources; i++)
+    {
+      source = pool->sources[i];
+      xfree(source->idarraydata);
+      xfree(source->rpmdbid);
+      xfree(source);
+    }
+  xfree(pool->solvables);
+  xfree(pool->sources);
+  xfree(pool->stringspace);
+  xfree(pool->strings);
+  xfree(pool->rels);
+  xfree(pool);
+}
+
+
+/*
+ * pool_prepare()
+ * 
+ * create hashes over complete pool to ease lookups
+ * 
+ */
+
+void
+pool_prepare(Pool *pool)
+{
+  int i, num, np, extra;
+  unsigned int n;
+  Offset off;
+  Solvable *s;
+  Id id;
+  Offset *idp;
+  Offset *whatprovides;
+  Id *whatprovidesdata, *d;
+
+  if (pool->verbose)
+    printf("number of solvables: %d\n", pool->nsolvables);
+  if (pool->verbose)
+    printf("number of ids: %d + %d\n", pool->nstrings, pool->nrels);
+
+  pool_freeidhashes(pool);
+  pool_freewhatprovides(pool);
+  num = pool->nstrings + pool->nrels;
+  whatprovides = (Offset *)xcalloc(num, sizeof(Offset));
+
+  /* count providers for each name */
+
+  for (i = 1; i < pool->nsolvables; i++)   /* loop over all, but first, solvables */
+    {
+      Id *pp;
+      s = pool->solvables + i;
+      if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+       continue;               /* sources do not provide anything */
+      if (pool->id2arch && (s->arch > pool->lastarch || !pool->id2arch[s->arch]))
+       continue;               /* architecture not installable */
+      pp = s->provides;
+      if (!pp)                 /* solvable does not provide anything */
+       continue;
+      while ((id = *pp++) != ID_NULL)
+       {
+         if (ISRELDEP(id))
+           {
+             Reldep *rd = GETRELDEP(pool, id);
+             id = rd->name;
+           }
+         whatprovides[id]++;          /* inc count of providers */
+       }
+    }
+
+  off = 2;     /* first entry is undef, second is empty list */
+  idp = whatprovides;
+  np = 0;                             /* number of names provided */
+  for (i = 0; i < num; i++, idp++)
+    {
+      n = *idp;
+      if (!n)                         /* no providers */
+       continue;
+      *idp = off;                     /* move from counts to offsets into whatprovidesdata */
+      off += n + 1;                   /* make space for all providers + terminating ID_NULL */
+      np++;                           /* inc # of provider 'slots' */
+    }
+
+  if (pool->verbose)
+    printf("provide ids: %d\n", np);
+  extra = 2 * pool->nrels;
+
+  if (extra < 256)
+    extra = 256;
+
+  if (pool->verbose)
+    printf("provide space needed: %d + %d\n", off, extra);
+
+  /* alloc space for all providers + extra */
+  whatprovidesdata = (Id *)xcalloc(off + extra, sizeof(Id));
+
+  /* now fill data for all provides */
+  for (i = 1; i < pool->nsolvables; i++)
+    {
+      Id *pp;
+      s = pool->solvables + i;
+      if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+       continue;               /* sources do not provide anything */
+      if (pool->id2arch && (s->arch > pool->lastarch || !pool->id2arch[s->arch]))
+       continue;               /* architecture not installable */
+      pp = s->provides;
+      if (!pp)                        /* solvable does not provide anything */
+       continue;
+
+      /* for all provides of this solvable */
+      while ((id = *pp++) != 0)
+       {
+         if (ISRELDEP(id))
+           {
+             Reldep *rd = GETRELDEP(pool, id);
+             id = rd->name;
+           }
+         d = whatprovidesdata + whatprovides[id];   /* offset into whatprovidesdata */
+         if (*d)
+           {
+             d++;
+             while (*d)               /* find free slot */
+               d++;
+             if (d[-1] == i)
+               {
+#if 0
+                 if (pool->verbose) printf("duplicate entry for %s in package %s.%s\n", id2str(pool, id), id2str(pool, s->name), id2str(pool, s->arch));
+#endif
+                 continue;
+               }
+           }
+         *d = i;                      /* put solvable Id into data */
+       }
+    }
+
+  pool->whatprovides = whatprovides;
+  pool->whatprovidesdata = whatprovidesdata;
+  pool->whatprovidesdataoff = off;
+  pool->whatprovidesdataleft = extra;
+}
+
+/******************************************************************************/
+
+/*
+ * pool_queuetowhatprovides
+ * 
+ * on-demand filling of provider information
+ * move queue data into whatprovidesdata
+ * q: queue of Ids
+ * returns: Offset into whatprovides
+ */
+
+Id
+pool_queuetowhatprovides(Pool *pool, Queue *q)
+{
+  Offset off;
+  int count = q->count;
+
+  if (count == 0)                     /* queue empty -> ID_EMPTY */
+    return ID_EMPTY;
+
+  /* extend whatprovidesdata if needed, +1 for ID_NULL-termination */
+  if (pool->whatprovidesdataleft < count + 1)
+    {
+      if (pool->verbose)
+        printf("growing provides hash data...\n");
+      pool->whatprovidesdata = (Id *)xrealloc(pool->whatprovidesdata, (pool->whatprovidesdataoff + count + 4096) * sizeof(Id));
+      pool->whatprovidesdataleft = count + 4096;
+    }
+
+  /* copy queue to next free slot */
+  off = pool->whatprovidesdataoff;
+  memcpy(pool->whatprovidesdata + pool->whatprovidesdataoff, q->elements, count * sizeof(Id));
+
+  /* adapt count and ID_NULL-terminate */
+  pool->whatprovidesdataoff += count;
+  pool->whatprovidesdata[pool->whatprovidesdataoff++] = ID_NULL;
+  pool->whatprovidesdataleft -= count + 1;
+
+  return (Id)off;
+}
+
+
+/*
+ * addrangedep
+ * 
+ * add RangeDep to whatprovides
+ * no exact providers, do range match
+ * 
+ */
+
+Id *
+addrelproviders(Pool *pool, Id d)
+{
+  Reldep *rd = GETRELDEP(pool, d);
+  Reldep *prd;
+  Queue plist;
+  Id buf[16];
+  Id name = rd->name;
+  Id evr = rd->evr;
+  int flags = rd->flags;
+  Id pid, *pidp;
+  Id p, *pp;
+
+  d = GETRELID(pool, d);
+  queueinit_buffer(&plist, buf, sizeof(buf)/sizeof(*buf));
+#if 0
+  if (pool->verbose)
+    printf("addrelproviders: what provides %s?\n", id2str(pool, name));
+#endif
+  if (flags)
+    {
+      FOR_PROVIDES(p, pp, name)
+       {
+#if 0
+         if (pool->verbose)
+           printf("addrelproviders: checking package %s\n", id2str(pool, pool->p[p].name));
+#endif
+         /* solvable p provides name in some rels */
+         for (pidp = pool->solvables[p].provides; (pid = *pidp++) != 0; )
+           {
+             int pflags;
+             Id pevr;
+
+             if (pid == name)
+               break;          /* yes, provides all versions */
+             if (!ISRELDEP(pid))
+               continue;               /* wrong provides name */
+             prd = GETRELDEP(pool, pid);
+             if (prd->name != name)
+               continue;               /* wrong provides name */
+             /* right package, both deps are rels */
+             pflags = prd->flags;
+             if (!pflags)
+               continue;
+             if (flags == 7 || pflags == 7)
+               break; /* included */
+             if ((pflags & flags & 5) != 0)
+               break; /* same direction, match */
+             pevr = prd->evr;
+             if (pevr == evr)
+               {
+                 if ((pflags & flags & 2) != 0)
+                   break; /* both have =, match */
+               }
+             else
+               {
+                 int f = flags == 5 ? 5 : flags == 2 ? pflags : (flags ^ 5) & (pflags | 5);
+                 if ((f & (1 << (1 + evrcmp(pool, pevr, evr)))) != 0)
+                   break;
+               }
+           }
+         if (!pid)
+           continue;   /* no rel match */
+         queuepush(&plist, p);
+       }
+    }
+  /* add providers to whatprovides */
+#if 0
+  if (pool->verbose) printf("addrelproviders: adding %d packages to %d\n", plist.count, d);
+#endif
+  pool->whatprovides[d] = pool_queuetowhatprovides(pool, &plist);
+  queuefree(&plist);
+
+  return pool->whatprovidesdata + pool->whatprovides[d];
+}
+
+
+/*
+ * return source of solvable
+ * or NULL
+ */
+
+Source *
+pool_source(Pool *pool, Solvable *s)
+{
+  int i;
+  Source *source;
+  int off = s - pool->solvables;
+
+  for (i = 0; i < pool->nsources; i++)
+    {
+      source = pool->sources[i];
+      if (off >= source->start
+         && off < source->start+source->nsolvables)
+      {
+       return source;
+      }
+    }
+  return NULL;
+}
+
+// EOF
diff --git a/src/pool.h b/src/pool.h
new file mode 100644 (file)
index 0000000..1a11e66
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * pool.h
+ * 
+ */
+
+#ifndef POOL_H
+#define POOL_H
+
+#include "pooltypes.h"
+#include "poolid.h"
+#include "source.h"
+#include "solvable.h"
+#include "queue.h"
+
+// bool
+typedef int bool;
+
+// see initpool_data[] in pool.c
+
+/* well known ids */
+#define ID_NULL                        0
+#define ID_EMPTY               1
+#define SOLVABLE_NAME           2
+#define SOLVABLE_ARCH           3
+#define SOLVABLE_EVR            4
+#define SOLVABLE_PROVIDES       5
+#define SOLVABLE_OBSOLETES      6
+#define SOLVABLE_CONFLICTS      7
+#define SOLVABLE_REQUIRES       8
+#define SOLVABLE_RECOMMENDS     9
+#define SOLVABLE_SUGGESTS       10
+#define SOLVABLE_SUPPLEMENTS    11
+#define SOLVABLE_ENHANCES       12
+#define SOLVABLE_FRESHENS       13
+#define RPM_RPMDBID             14
+#define SOLVABLE_PREREQMARKER   15             // normal requires before this, prereqs after this
+#define SOLVABLE_FILEMARKER     16             // normal provides before this, generated file provides after this
+#define ARCH_SRC                17
+#define ARCH_NOSRC              18
+#define ARCH_NOARCH             19
+
+//-----------------------------------------------
+
+struct _Pool {
+  int verbose;         // pool is used everywhere, so put the verbose flag here
+
+  Offset *strings;            // table of offsets into stringspace, indexed by Id: Id -> Offset
+  int nstrings;               // number of unique strings in stringspace
+  char *stringspace;          // space for all unique strings: stringspace + Offset = string
+  Offset sstrings;            // next free pos in stringspace
+
+  Hashtable stringhashtbl;    // hash table: (string ->) Hash -> Id
+  Hashmask stringhashmask;    // modulo value for hash table (size of table - 1)
+
+  Reldep *rels;               // table of rels: Id -> Reldep
+  int nrels;                  // number of unique rels
+  Hashtable relhashtbl;       // hash table: (name,evr,op ->) Hash -> Id
+  Hashmask relhashmask;
+
+  Source **sources;
+  int nsources;
+
+  Solvable *solvables;
+  int nsolvables;
+
+  bool promoteepoch;
+
+  Id *id2arch;                 /* map arch ids to scores */
+  Id lastarch;                 /* last valid entry in id2arch */
+
+  /* providers data, as two-step indirect list
+   * whatprovides[Id] -> Offset into whatprovidesdata for name
+   * whatprovidesdata[Offset] -> ID_NULL-terminated list of solvables providing Id
+   */
+  Offset *whatprovides;                /* Offset to providers of a specific name, Id -> Offset  */
+  Id *whatprovidesdata;                /* Ids of solvable providing Id */
+  Offset whatprovidesdataoff;  /* next free slot within whatprovidesdata */
+  int whatprovidesdataleft;    /* number of 'free slots' within whatprovidesdata */
+};
+
+#define TYPE_ID                        1
+#define TYPE_IDARRAY           2
+#define TYPE_STR               3
+#define TYPE_U32               4
+#define TYPE_BITMAP            128
+
+
+//-----------------------------------------------
+
+// whatprovides
+//  "foo" -> Id -> lookup in table, returns offset into whatprovidesdata where array of Ids providing 'foo' are located, 0-terminated
+
+
+#define GET_PROVIDESP(d, v) (ISRELDEP(d) ?                             \
+             (v = GETRELID(pool, d), pool->whatprovides[v] ?           \
+               pool->whatprovidesdata + pool->whatprovides[v] :                \
+              addrelproviders(pool, d)                                 \
+             ) :                                                       \
+             (pool->whatprovidesdata + pool->whatprovides[d]))
+
+/* loop over all providers of d */
+#define FOR_PROVIDES(v, vp, d)                                                 \
+  for (vp = GET_PROVIDESP(d, v) ; (v = *vp++) != ID_NULL; )
+
+/* mark dependencies with relation by setting bit31 */
+
+#define MAKERELDEP(id) ((id) | 0x80000000)
+#define ISRELDEP(id) (((id) & 0x80000000) != 0)
+#define GETRELID(pool, id) ((pool)->nstrings + ((id) ^ 0x80000000))     /* returns Id  */
+#define GETRELDEP(pool, id) ((pool)->rels + ((id) ^ 0x80000000))       /* returns Reldep* */
+
+#define REL_GT 1
+#define REL_EQ 2
+#define REL_LT 4
+
+extern Pool *pool_create(void);
+extern void pool_free(Pool *pool);
+extern void pool_freesource(Pool *pool, Source *source);
+extern void pool_prepare(Pool *pool);
+extern void pool_freewhatprovides(Pool *pool);
+extern Id pool_queuetowhatprovides(Pool *pool, Queue *q);
+
+extern Id *addrelproviders(Pool *pool, Id d);
+
+extern Source *pool_source(Pool *pool, Solvable *s);
+
+#endif /* POOL_H */
diff --git a/src/poolarch.c b/src/poolarch.c
new file mode 100644 (file)
index 0000000..ec41013
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * poolarch.c
+ * 
+ * create architecture policies
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "poolid.h"
+#include "poolarch.h"
+#include "util.h"
+
+const char *archpolicies[] = {
+  "x86_64", "x86_64:i686:i585:i486:i386",
+  "i686",   "i686:i585:i486:i386",
+  "i586",   "i585:i486:i386",
+  "i486",   "i486:i386",
+  "i386",   "i386",
+  0
+};
+
+void
+pool_setarch(Pool *pool, const char *arch)
+{
+  const char *a;
+  char buf[256];
+  unsigned int score = 0x10001;
+  size_t l;
+  char d;
+  int i;
+  Id *id2arch;
+  Id id, lastarch;
+  
+  pool->id2arch = xfree(pool->id2arch);
+  if (!arch)
+    {
+      pool->lastarch = 0;
+      return;
+    }
+  id = ARCH_NOARCH;
+  lastarch = id + 255;
+  id2arch = xcalloc(lastarch + 1, sizeof(Id));
+  id2arch[id] = 1;
+
+  a = "";
+  for (i = 0; archpolicies[i]; i += 2)
+    if (!strcmp(archpolicies[i], arch))
+      break;
+  if (archpolicies[i])
+    a = archpolicies[i + 1];
+  d = 0;
+  while (*a)
+    {
+      l = strcspn(a, ":=>");
+      if (l && l < sizeof(buf) - 1)
+       {
+         strncpy(buf, a, l);
+         buf[l] = 0;
+         id = str2id(pool, buf, 1);
+         if (id > lastarch)
+           {
+             id2arch = xrealloc(id2arch, (id + 255 + 1) * sizeof(Id));
+             memset(id2arch + lastarch + 1, 0, (id + 255 - lastarch) * sizeof(Id));
+             lastarch = id + 255;
+           }
+         if (id2arch[id] == 0)
+           {
+             if (d == ':')
+               score += 0x10000;
+             else if (d == '>')
+               score += 0x00001;
+             id2arch[id] = score;
+           }
+       }
+      a += l;
+      if ((d = *a++) == 0)
+       break;
+    }
+  pool->id2arch = id2arch;
+  pool->lastarch = lastarch;
+}
diff --git a/src/poolarch.h b/src/poolarch.h
new file mode 100644 (file)
index 0000000..cd4f044
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef POOLARCH_H
+#define POOLARCH_H
+
+#include "pool.h"
+
+extern void pool_setarch(Pool *, const char *);
+
+#endif /* POOLARCH_H */
diff --git a/src/poolid.c b/src/poolid.c
new file mode 100644 (file)
index 0000000..a598514
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * poolid.c
+ *
+ * Id management
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "pool.h"
+#include "poolid.h"
+#include "poolid_private.h"
+#include "util.h"
+
+
+// intern string into pool
+// return Id
+
+Id
+str2id(Pool *pool, const char *str, int create)
+{
+  Hashval h;
+  unsigned int hh;
+  Hashmask hashmask;
+  int i, space_needed;
+  Id id;
+  Hashtable hashtbl;
+
+  // check string
+  if (!str)
+    return ID_NULL;
+  if (!*str)
+    return ID_EMPTY;
+
+  hashmask = pool->stringhashmask;
+  hashtbl = pool->stringhashtbl;
+
+  // expand hashtable if needed
+  // 
+  // 
+  if (pool->nstrings * 2 > hashmask)
+    {
+      xfree(hashtbl);
+
+      // realloc hash table
+      pool->stringhashmask = hashmask = mkmask(pool->nstrings + STRING_BLOCK);
+      pool->stringhashtbl = hashtbl = (Hashtable)xcalloc(hashmask + 1, sizeof(Id));
+
+      // rehash all strings into new hashtable
+      for (i = 1; i < pool->nstrings; i++)
+       {
+         h = strhash(pool->stringspace + pool->strings[i]) & hashmask;
+         hh = HASHCHAIN_START;
+         while (hashtbl[h] != ID_NULL)  // follow overflow chain
+           h = HASHCHAIN_NEXT(h, hh, hashmask);
+         hashtbl[h] = i;
+       }
+    }
+
+  // compute hash and check for match
+
+  h = strhash(str) & hashmask;
+  hh = HASHCHAIN_START;
+  while ((id = hashtbl[h]) != ID_NULL)  // follow hash overflow chain
+    {
+      // break if string already hashed
+      if(!strcmp(pool->stringspace + pool->strings[id], str))
+       break;
+      h = HASHCHAIN_NEXT(h, hh, hashmask);
+    }
+  if (id || !create)    // exit here if string found
+    return id;
+
+  pool_freewhatprovides(pool);
+
+  // generate next id and save in table
+  id = pool->nstrings++;
+  hashtbl[h] = id;
+
+  // 
+  if ((id & STRING_BLOCK) == 0)
+    pool->strings = xrealloc(pool->strings, ((pool->nstrings + STRING_BLOCK) & ~STRING_BLOCK) * sizeof(Hashval));
+  // 'pointer' into stringspace is Offset of next free pos: sstrings
+  pool->strings[id] = pool->sstrings;
+
+  space_needed = strlen(str) + 1;
+
+  // resize string buffer if needed
+  if (((pool->sstrings + space_needed - 1) | STRINGSPACE_BLOCK) != ((pool->sstrings - 1) | STRINGSPACE_BLOCK))
+    pool->stringspace = xrealloc(pool->stringspace, (pool->sstrings + space_needed + STRINGSPACE_BLOCK) & ~STRINGSPACE_BLOCK);
+  // copy new string into buffer
+  memcpy(pool->stringspace + pool->sstrings, str, space_needed);
+  // next free pos is behind new string
+  pool->sstrings += space_needed;
+
+  return id;
+}
+
+
+Id
+rel2id(Pool *pool, Id name, Id evr, int flags, int create)
+{
+  Hashval h;
+  unsigned int hh;
+  Hashmask hashmask;
+  int i;
+  Id id;
+  Hashtable hashtbl;
+  Reldep *ran;
+
+  hashmask = pool->relhashmask;
+  hashtbl = pool->relhashtbl;
+  ran = pool->rels;
+  
+  // extend hashtable if needed
+  if (pool->nrels * 2 > hashmask)
+    {
+      xfree(pool->relhashtbl);
+      pool->relhashmask = hashmask = mkmask(pool->nstrings + REL_BLOCK);
+      pool->relhashtbl = hashtbl = xcalloc(hashmask + 1, sizeof(Id));
+      // rehash all rels into new hashtable
+      for (i = 1; i < pool->nrels; i++)
+       {
+         h = relhash(ran[i].name, ran[i].evr, ran[i].flags) & hashmask;
+         hh = HASHCHAIN_START;
+         while (hashtbl[h])
+           h = HASHCHAIN_NEXT(h, hh, hashmask);
+         hashtbl[h] = i;
+       }
+    }
+  
+  // compute hash and check for match
+
+  h = relhash(name, evr, flags) & hashmask;
+  hh = HASHCHAIN_START;
+  while ((id = hashtbl[h]) != 0)
+    {
+      if (ran[id].name == name && ran[id].evr == evr && ran[id].flags == flags)
+       break;
+      h = HASHCHAIN_NEXT(h, hh, hashmask);
+    }
+  if (id)
+    return MAKERELDEP(id);
+
+  if (!create)
+    return ID_NULL;
+
+  pool_freewhatprovides(pool);
+
+  id = pool->nrels++;
+  // extend rel space if needed
+  if ((id & REL_BLOCK) == 0)
+    pool->rels = xrealloc(pool->rels, ((pool->nrels + REL_BLOCK) & ~REL_BLOCK) * sizeof(Reldep));
+  hashtbl[h] = id;
+  ran = pool->rels + id;
+  ran->name = name;
+  ran->evr = evr;
+  ran->flags = flags;
+  return MAKERELDEP(id);
+}
+
+
+// Id -> String
+// for rels (returns name only) and strings
+// 
+const char *
+id2str(Pool *pool, Id id)
+{
+  if (ISRELDEP(id))
+    {
+      Reldep *rd = GETRELDEP(pool, id);
+      return pool->stringspace + pool->strings[rd->name];
+    }
+  return pool->stringspace + pool->strings[id];
+}
+
+static const char *rels[] = {
+  " ! ",
+  " > ",
+  " = ",
+  " >= ",
+  " < ",
+  " <> ",
+  " <= ",
+  " <=> "
+};
+
+
+// get operator for RelId
+const char *
+id2rel(Pool *pool, Id id)
+{
+  Reldep *rd;
+  if (!ISRELDEP(id))
+    return "";
+  rd = GETRELDEP(pool, id);
+  return rels[rd->flags & 7];
+}
+
+
+// get e:v.r for Id
+// 
+const char *
+id2evr(Pool *pool, Id id)
+{
+  Reldep *rd;
+  if (!ISRELDEP(id))
+    return "";
+  rd = GETRELDEP(pool, id);
+  return pool->stringspace + pool->strings[rd->evr];
+}
+
+void
+pool_shrink_strings(Pool *pool)
+{
+  pool->stringspace = (char *)xrealloc(pool->stringspace, (pool->sstrings + STRINGSPACE_BLOCK) & ~STRINGSPACE_BLOCK);
+  pool->strings = (Offset *)xrealloc(pool->strings, ((pool->nstrings + STRING_BLOCK) & ~STRING_BLOCK) * sizeof(Offset));
+}
+
+void
+pool_shrink_rels(Pool *pool)
+{
+  pool->rels = (Reldep *)xrealloc(pool->rels, ((pool->nrels + REL_BLOCK) & ~REL_BLOCK) * sizeof(Reldep));
+}
+
+// reset all hash tables
+// 
+void
+pool_freeidhashes(Pool *pool)
+{
+  pool->stringhashtbl = xfree(pool->stringhashtbl);
+  pool->relhashtbl = xfree(pool->relhashtbl);
+  pool->stringhashmask = 0;
+  pool->relhashmask = 0;
+}
+
+// EOF
diff --git a/src/poolid.h b/src/poolid.h
new file mode 100644 (file)
index 0000000..087d113
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * poolid.h
+ * 
+ */
+
+#ifndef POOLID_H
+#define POOLID_H
+
+#include "pooltypes.h"
+#include "hash.h"
+
+//-----------------------------------------------
+// Id's with relation
+
+typedef struct _Reldep {
+  Id name;             // "package"
+  Id evr;              // "0:42-3"
+  int flags;           // operation/relation, see REL_x below
+} Reldep;
+
+extern Id str2id(Pool *pool, const char *, int);
+extern Id rel2id(Pool *pool, Id, Id, int, int);
+extern const char *id2str(Pool *pool, Id);
+extern const char *id2rel(Pool *pool, Id);
+extern const char *id2evr(Pool *pool, Id);
+
+extern void pool_shrink_strings(Pool *pool);
+extern void pool_shrink_rels(Pool *pool);
+extern void pool_freeidhashes(Pool *pool);
+
+#endif /* POOLID_H */
diff --git a/src/poolid_private.h b/src/poolid_private.h
new file mode 100644 (file)
index 0000000..c2e4807
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * poolid_private.h
+ * 
+ */
+
+#ifndef POOLID_PRIVATE_H
+#define POOLID_PRIVATE_H
+
+// the size of all buffers is incremented in blocks
+// these are the block values (increment values) for the
+// string hashtable, rel hashtable, stringspace buffer and idarray
+// 
+#define STRING_BLOCK      2047          // hashtable for strings
+#define REL_BLOCK         1023          // hashtable for relations
+#define STRINGSPACE_BLOCK 65535         // string buffer
+
+#endif /* POOLID_PRIVATE_H */
diff --git a/src/pooltypes.h b/src/pooltypes.h
new file mode 100644 (file)
index 0000000..3231b07
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * pooltypes.h
+ * 
+ */
+
+#ifndef POOLTYPES_H
+#define POOLTYPES_H
+
+/* version number for .solv files */
+#define SOLV_VERSION 0
+
+struct _Pool;
+typedef struct _Pool Pool;
+
+// identifier for string values
+typedef int Id;                /* must be signed!, since negative Id is used in solver rules to denote negation */
+
+// offset value, e.g. used to 'point' into the stringspace
+typedef unsigned int Offset;
+
+#endif /* POOLTYPES_H */
diff --git a/src/queue.c b/src/queue.c
new file mode 100644 (file)
index 0000000..1b52a8c
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * queue.c
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "queue.h"
+
+void
+clonequeue(Queue *t, Queue *s)
+{
+  t->alloc = t->elements = malloc((s->count + 8) * sizeof(Id));
+  if (s->count)
+    memcpy(t->alloc, s->elements, s->count * sizeof(Id));
+  t->count = s->count;
+  t->left = 8;
+}
+
+void
+queueinit(Queue *q)
+{
+  q->alloc = q->elements = 0;
+  q->count = q->left = 0;
+}
+
+void
+queueinit_buffer(Queue *q, Id *buf, int size)
+{
+  q->alloc = 0;
+  q->elements = buf;
+  q->count = 0;
+  q->left = size;
+}
+
+void
+queuefree(Queue *q)
+{
+  if (q->alloc)
+    free(q->alloc);
+  q->alloc = q->elements = 0;
+  q->count = q->left = 0;
+}
+
+Id
+queueshift(Queue *q)
+{
+  if (!q->count)
+    return 0;
+  q->count--;
+  return *q->elements++;
+}
+
+void
+queuepush(Queue *q, Id id)
+{
+  if (!q->left)
+    {
+      if (q->alloc && q->alloc != q->elements)
+       {
+         memmove(q->alloc, q->elements, q->count * sizeof(Id));
+         q->left += q->elements - q->alloc;
+         q->elements = q->alloc;
+       }
+      else if (q->alloc)
+       {
+         q->elements = q->alloc = realloc(q->alloc, (q->count + 8) * sizeof(Id));
+         q->left += 8;
+       }
+      else
+       {
+         q->alloc = malloc((q->count + 8) * sizeof(Id));
+         if (q->count)
+           memcpy(q->alloc, q->elements, q->count * sizeof(Id));
+         q->elements = q->alloc;
+         q->left += 8;
+       }
+    }
+  q->elements[q->count++] = id;
+  q->left--;
+}
+
+void
+queuepushunique(Queue *q, Id id)
+{
+  int i;
+  for (i = q->count; i > 0; )
+    if (q->elements[--i] == id)
+      return;
+  queuepush(q, id);
+}
+
+
diff --git a/src/queue.h b/src/queue.h
new file mode 100644 (file)
index 0000000..dd70443
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * queue.h
+ * 
+ */
+
+#ifndef QUEUE_H
+#define QUEUE_H
+
+#include "pooltypes.h"
+
+typedef struct _Queue {
+  Id *elements;                // current elements
+  int count;           // current number of elements (minimal size for elements pointer)
+  Id *alloc;           // this is whats actually allocated, elements > alloc if shifted
+  int left;            // space left in alloc *after* elements+count
+} Queue;
+
+// clear queue
+
+#define QUEUEEMPTY(q) ((q)->alloc ? ((q)->left += ((q)->elements - (q)->alloc) + (q)->count, (q)->elements = (q)->alloc, (q)->count = 0) : ((q)->left += (q)->count, (q)->count = 0))
+
+extern void clonequeue(Queue *t, Queue *s);
+extern void queueinit(Queue *q);
+extern void queueinit_buffer(Queue *q, Id *buf, int size);
+extern void queuefree(Queue *q);
+extern Id queueshift(Queue *q);
+extern void queuepush(Queue *q, Id id);
+extern void queuepushunique(Queue *q, Id id);
+
+#endif /* QUEUE_H */
diff --git a/src/solvable.h b/src/solvable.h
new file mode 100644 (file)
index 0000000..aaecd71
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * solvable.h
+ * 
+ * A solvable represents an object with name-epoch:version-release.arch and dependencies
+ */
+
+#ifndef SOLVABLE_H
+#define SOLVABLE_H
+
+#include "pooltypes.h"
+
+typedef struct _Solvable {
+  Id name;
+  Id arch;
+  Id evr;
+
+  // dependencies are pointers into idarray of source the solvable originates from
+  Id *provides;                        // terminated with Id 0
+  Id *obsoletes;
+  Id *conflicts;
+
+  Id *requires;
+  Id *recommends;
+  Id *suggests;
+
+  Id *supplements;
+  Id *enhances;
+
+  Id *freshens;
+} Solvable;
+
+#endif /* SOLVABLE_H */
diff --git a/src/solver.c b/src/solver.c
new file mode 100644 (file)
index 0000000..4a875b8
--- /dev/null
@@ -0,0 +1,2513 @@
+/*
+ * solver.c
+ *
+ * SAT based dependency solver
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "solver.h"
+#include "bitmap.h"
+#include "pool.h"
+#include "util.h"
+#include "evr.h"
+
+#define RULES_BLOCK 63
+
+static Pool *prune_best_version_arch_sortcmp_data;
+
+/*-----------------------------------------------------------------*/
+
+/*
+ * prep for prune_best_version_arch
+ *   sort by name
+ */
+
+static int
+prune_best_version_arch_sortcmp(const void *ap, const void *bp)
+{
+  Pool *pool = prune_best_version_arch_sortcmp_data;
+  Id a = *(Id *)ap;
+  Id b = *(Id *)bp;
+  return pool->solvables[a].name - pool->solvables[b].name;
+}
+
+
+#if 0
+static Id
+replaces_system(Solver *solv, Id id)
+{
+  Pool *pool = solv->pool;
+  Source *system = solv->system;
+  Id *name = pool->solvables[id].name;
+
+  FOR_PROVIDES(p, pp, id)
+    {
+      s = pool->solvables + p;
+      if (s->name != name)
+       continue;
+      if (p >= system->start && p < system->start + system->nsolvables)
+       return p;
+    }
+}
+#endif
+
+/*
+ * prune_best_version_arch
+ * 
+ * sort list of packages (given through plist) by name and evr
+ * return result through plist
+ * 
+ */
+
+/* FIXME: must also look at update packages */
+
+void
+prune_best_version_arch(Pool *pool, Queue *plist)
+{
+  Id best = ID_NULL;
+  int i, j;
+  Solvable *s;
+  Id a, bestscore;
+
+  if (plist->count < 2)                /* no need to prune for a single entry */
+    return;
+  if (pool->verbose) printf("prune_best_version_arch %d\n", plist->count);
+
+  /* prune to best architecture */
+  if (pool->id2arch)
+    {
+      bestscore = 0;
+      for (i = 0; i < plist->count; i++)
+       {
+         s = pool->solvables + plist->elements[i];
+         a = s->arch;
+         if (a > pool->lastarch)
+           continue;
+         a = pool->id2arch[a];
+         if ((a & 0xffff0000) > bestscore)
+           bestscore = a & 0xffff0000;
+       }
+      for (i = j = 0; i < plist->count; i++)
+       {
+         s = pool->solvables + plist->elements[i];
+         a = s->arch;
+         if (a > pool->lastarch)
+           continue;
+         a = pool->id2arch[a];
+         /* a == 1 -> noarch */
+         if (a != 1 && (a & 0xffff0000) != bestscore)
+           continue;
+         plist->elements[j++] = plist->elements[i];
+       }
+      plist->count = j;
+      if (j == 0)
+       return;
+    }
+
+  prune_best_version_arch_sortcmp_data = pool;
+  /* sort by name first */
+  qsort(plist->elements, plist->count, sizeof(Id), prune_best_version_arch_sortcmp);
+
+  /* now find best 'per name' */
+  for (i = j = 0; i < plist->count; i++)
+    {
+      s = pool->solvables + plist->elements[i];
+      if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+       continue;
+
+      if (pool->verbose) printf("- %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+
+      if (!best)                      /* if no best yet, the current is best */
+        {
+          best = plist->elements[i];
+          continue;
+        }
+
+      /* name switch: re-init */
+      if (pool->solvables[best].name != s->name)   /* new name */
+        {
+          if (pool->verbose) printf("BEST: %s-%s.%s\n", id2str(pool, pool->solvables[best].name), id2str(pool, pool->solvables[best].evr), id2str(pool, pool->solvables[best].arch));
+          plist->elements[j++] = best; /* move old best to front */
+          best = plist->elements[i];   /* take current as new best */
+          continue;
+        }
+
+      if (pool->solvables[best].evr != s->evr)   /* compare evr */
+        {
+          if (evrcmp(pool, pool->solvables[best].evr, s->evr) < 0)
+            best = plist->elements[i];
+        }
+    }
+
+  if (best == ID_NULL)
+    best = plist->elements[0];
+
+  /* XXX also check obsoletes! */
+  if (pool->verbose) printf("BEST: %s-%s.%s\n", id2str(pool, pool->solvables[best].name), id2str(pool, pool->solvables[best].evr), id2str(pool, pool->solvables[best].arch));
+
+  plist->elements[j++] = best;
+  plist->count = j;
+
+}
+
+/*-----------------------------------------------------------------*/
+
+/*
+ * print rules
+ */
+
+static void
+printruleelement(Solver *solv, Rule *r, Id v)
+{
+  Pool *pool = solv->pool;
+  Solvable *s;
+  if (v < 0)
+    {
+      s = pool->solvables + -v;
+      printf("    !%s-%s.%s [%d]", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch), -v);
+    }
+  else
+    {
+      s = pool->solvables + v;
+      printf("    %s-%s.%s [%d]", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch), v);
+    }
+  if (r)
+    {
+      if (r->w1 == v)
+       printf(" (w1)");
+      if (r->w2 == v)
+       printf(" (w2)");
+    }
+  if (solv->decisionmap[s - pool->solvables] > 0)
+    printf(" I.%d", solv->decisionmap[s - pool->solvables]);
+  if (solv->decisionmap[s - pool->solvables] < 0)
+    printf(" C.%d", -solv->decisionmap[s - pool->solvables]);
+  printf("\n");
+}
+
+
+/*
+ * print rule
+ */
+
+static void
+printrule(Solver *solv, Rule *r)
+{
+  int i;
+  Id v;
+
+  if (r >= solv->rules && r < solv->rules + solv->nrules)   /* r is a solver rule */
+    printf("Rule #%d:\n", (int)(r - solv->rules));
+  else
+    printf("Rule:\n");                /* r is any rule */
+  for (i = 0; ; i++)
+    {
+      if (i == 0)
+       v = r->p;
+      else if (r->d == ID_NULL)
+       {
+         if (i == 2)
+           break;
+         v = r->w2;
+       }
+      else
+       v = solv->pool->whatprovidesdata[r->d + i - 1];
+      if (v == ID_NULL)
+       break;
+      printruleelement(solv, r, v);
+    }
+  printf("    next: %d %d\n", r->n1, r->n2);
+}
+
+
+/*-----------------------------------------------------------------*/
+
+/*
+ * Rule handling
+ */
+
+static Pool *unifyrules_sortcmp_data;
+
+/*
+ * compare rules for unification sort
+ */
+
+static int
+unifyrules_sortcmp(const void *ap, const void *bp)
+{
+  Pool *pool = unifyrules_sortcmp_data;
+  Rule *a = (Rule *)ap;
+  Rule *b = (Rule *)bp;
+  Id *ad, *bd;
+  int x;
+  
+  x = a->p - b->p;
+  if (x)
+    return x;                         /* p differs */
+
+  /* identical p */
+  if (a->d == 0 && b->d == 0)
+    return a->w2 - b->w2;             /* assertion: return w2 diff */
+
+  if (a->d == 0)                      /* a is assertion, b not */
+    {
+      x = a->w2 - pool->whatprovidesdata[b->d];
+      return x ? x : -1;
+    }
+
+  if (b->d == 0)                      /* b is assertion, a not */
+    {
+      x = pool->whatprovidesdata[a->d] - b->w2;
+      return x ? x : 1;
+    }
+
+  /* compare whatprovidesdata */
+  ad = pool->whatprovidesdata + a->d;
+  bd = pool->whatprovidesdata + b->d;
+  for (; *ad && *ad == *bd; ad++, bd++)
+    ;
+  return *ad - *bd;
+}
+
+
+/*
+ * unify rules
+ */
+
+static void
+unifyrules(Solver *solv)
+{
+  int i, j;
+  Rule *ir, *jr;
+
+  if (solv->nrules <= 1)              /* nothing to unify */
+    return;
+
+  /* sort rules first */
+  unifyrules_sortcmp_data = solv->pool;
+  qsort(solv->rules + 1, solv->nrules - 1, sizeof(Rule), unifyrules_sortcmp);
+
+  /* prune rules
+   * i = unpruned
+   * j = pruned
+   */
+  jr = 0;
+  for (i = j = 1, ir = solv->rules + 1; i < solv->nrules; i++, ir++)
+    {
+      if (jr && !unifyrules_sortcmp(ir, jr))
+       continue;                      /* prune! */
+      jr = solv->rules + j++;         /* keep! */
+      if (ir != jr)
+        *jr = *ir;
+    }
+
+  /* reduced count from nrules to j rules */
+  if (solv->pool->verbose) printf("pruned rules from %d to %d\n", solv->nrules, j);
+
+  /* adapt rule buffer */
+  solv->rules = (Rule *)xrealloc(solv->rules, ((solv->nrules + RULES_BLOCK) & ~RULES_BLOCK) * sizeof(Rule));
+  solv->nrules = j;
+#if 1
+  {
+    int binr = 0;
+    int dc = 0;
+    Id *dp;
+    Rule *r;
+
+    for (i = 1; i < solv->nrules; i++)
+      {
+       r = solv->rules + i;
+       if (r->d == 0)                 /* assertion */
+         {
+           binr++;
+           continue;
+         }
+        dp = solv->pool->whatprovidesdata + r->d;
+       while (*dp++)
+         dc++;
+      }
+    if (solv->pool->verbose)
+      {
+        printf("  binary: %d\n", binr);
+        printf("  normal: %d\n", solv->nrules - 1 - binr);
+        printf("  normal lits: %d\n", dc);
+      }
+  }
+#endif
+}
+
+#if 0
+
+/*
+ * hash rule
+ */
+
+static Hashval
+hashrule(Solver *solv, Id p, Id d, int n)
+{
+  unsigned int x = (unsigned int)p;
+  int *dp;
+
+  if (n <= 1)
+    return (x * 37) ^ (unsigned int)d; 
+  dp = solv->pool->whatprovidesdata + d;
+  while (*dp)
+    x = (x * 37) ^ (unsigned int)*dp++;
+  return x;
+}
+#endif
+
+
+/*
+ * add rule
+ *  p = direct literal; > 0 for learnt, < 0 for installed pkg (rpm)
+ *  d, if < 0 direct literal, if > 0 offset into whatprovides, if == 0 rule is assertion (look at p only)
+ *
+ *
+ * A requires b, b provided by B1,B2,B3 => (-A|B1|B2|B3)
+ * 
+ * p < 0 : rule from rpm (installed pkg)
+ * d > 0 : Offset in whatprovidesdata (list of providers)
+ * 
+ * A conflicts b, b provided by B1,B2,B3 => (-A|-B1), (-A|-B2), (-A|-B3)
+ *  d < 0: Id of solvable (e.g. B1)
+ * 
+ * d == 0: unary rule, assertion => (A) or (-A)
+ * 
+ *   Install:    p > 0, d = 0   (A)             user requested install
+ *   Remove:     p < 0, d = 0   (-A)            user requested remove
+ *   Requires:   p < 0, d > 0   (-A|B1|B2|...)  d: <list of providers for requirement of p>
+ *   Updates:    p > 0, d > 0   (A|B1|B2|...)   d: <list of updates for solvable p>
+ *   Conflicts:  p < 0, d < 0   (-A|-B)         either p (conflict issuer) or d (conflict provider)
+ *   ?           p > 0, d < 0   (A|-B)
+ *   No-op ?:    p = 0, d = 0   (null)          (used as policy rule placeholder)
+ */
+
+static Rule *
+addrule(Solver *solv, Id p, Id d)
+{
+  Rule *r = NULL;
+  Id *dp = NULL;
+
+  int n = 0;                          /* number of literals in rule - 1
+                                         0 = direct assertion (single literal)
+                                         1 = binary rule
+                                       */
+
+  /* it often happenes that requires lead to adding the same rpm rule
+   * multiple times, so we prune those duplicates right away to make
+   * the work for unifyrules a bit easier */
+
+  if (solv->nrules && !solv->jobrules)
+    {
+      r = solv->rules + solv->nrules - 1;   /* get the last added rule */
+      if (r->p == p && r->d == d && d != 0)   /* identical and not user requested */
+       return r;
+    }
+
+  if (d < 0)
+    {
+      if (p == d)
+       return NULL;                   /* ignore self conflict */
+      n = 1;
+    }
+  else if (d == 0)                    /* user requested */
+    n = 0;
+  else
+    {
+      for (dp = solv->pool->whatprovidesdata + d; *dp; dp++, n++)
+       if (*dp == -p)
+         return NULL;  /* rule is self-fulfilling */
+      if (n == 1)
+       d = dp[-1];
+    }
+
+  if (n == 0)                         /* direct assertion */
+    {
+      if (!solv->jobrules)
+       {
+         /* this is a rpm rule assertion, we do not have to allocate it */
+          /* we can identify it by looking at the decision level, it will be 1 */
+         if (p > 0)                       /*  */
+           abort();
+         if (solv->decisionmap[-p] > 0)   /*  */
+           abort();
+         if (solv->decisionmap[-p])       /*  */
+           return NULL;
+         queuepush(&solv->decisionq, p);
+         queuepush(&solv->decisionq_why, 0);
+         solv->decisionmap[-p] = -1;
+
+         return NULL;
+       }
+    }
+  else if (n == 1 && p > d)
+    {
+      /* smallest literal first so we can find dups */
+      n = p;
+      p = d;
+      d = n;
+      n = 1;                          /* re-set n, was used as temp var */
+    }
+
+  if (r
+      && n == 1
+      && r->p == p
+      && r->w2 == d)
+  {
+    return r;
+  }
+
+  if (r
+      && r->d
+      && n > 1
+      && r->p == p)
+    {
+      Id *dp2 = solv->pool->whatprovidesdata + r->d;
+      for (dp = solv->pool->whatprovidesdata + d; *dp; dp++, dp2++)
+      {
+       if (*dp != *dp2)
+         break;
+      }
+      if (*dp == *dp2)
+       return r;
+   }
+  
+  /*
+   * allocate new rule
+   */
+
+  /* check and extend rule buffer */
+  if ((solv->nrules & RULES_BLOCK) == 0)
+    {
+      solv->rules = (Rule *)xrealloc(solv->rules, (solv->nrules + (RULES_BLOCK + 1)) * sizeof(Rule));
+    }
+
+  r = solv->rules + solv->nrules++;    /* point to rule space */
+
+  r->p = p;
+  if (n == 0)
+    {
+      /* direct assertion, no watch needed */
+      r->d = 0;
+      r->w1 = p;
+      r->w2 = 0;
+    }
+  else if (n == 1)
+    {
+      /* binary rule */
+      r->d = 0;
+      r->w1 = p;
+      r->w2 = d;
+    }
+  else
+    {
+      r->d = d;
+      r->w1 = p;
+      r->w2 = solv->pool->whatprovidesdata[d];
+    }
+  r->n1 = 0;
+  r->n2 = 0;
+
+  /* we don't add the decision for learnt rules, as the code does that
+   * right after calling addrule anyway */
+  if (n == 0
+      && p
+      && !solv->learntrules)
+    {
+      /* must be system or job rule, as there are only negative unary rpm rules */
+      Id vv = p > 0 ? p : -p;
+      if (solv->decisionmap[vv])
+       {
+         int i;
+         if (solv->decisionmap[vv] > 0 && p > 0)
+           return r;
+         if (solv->decisionmap[vv] < 0 && p < 0)
+           return r;
+         /* direct conflict! */
+         for (i = 0; i < solv->decisionq.count; i++)
+         {
+           if (solv->decisionq.elements[i] == -p)
+             break;
+         }
+         if (i == solv->decisionq.count)
+           abort();
+         if (solv->decisionq_why.elements[i] == 0)
+           {
+             /* conflict with rpm rule */
+             queuepush(&solv->problems, r - solv->rules);
+             queuepush(&solv->problems, 0);
+             r->w1 = 0;        /* disable */
+             return r;
+           }
+         /* conflict with other job or system rule */
+         queuepush(&solv->problems, solv->decisionq_why.elements[i]);
+         queuepush(&solv->problems, r - solv->rules);
+         queuepush(&solv->problems, 0);
+         r->w1 = 0;    /* disable */
+          /* also disable conflicting rule */
+         solv->rules[solv->decisionq_why.elements[i]].w1 = 0;
+         /* XXX: remove from decisionq! */
+printf("XXX remove from decisionq\n");
+         return r;
+       }
+      queuepush(&solv->decisionq, p);
+      queuepush(&solv->decisionq_why, r - solv->rules);
+      solv->decisionmap[p > 0 ? p : -p] = p > 0 ? 1 : -1;
+    }
+  return r;
+}
+
+
+/*
+ * add (install) rules for solvable
+ * 
+ */
+
+static void
+addrulesforsolvable(Solver *solv, Solvable *s, Map *m)
+{
+  Pool *pool = solv->pool;
+  Source *system = solv->system;
+  Queue q;
+  int i;
+  int dontfix;
+  Id req, *reqp;
+  Id con, *conp;
+  Id obs, *obsp;
+  Id rec, *recp;
+  Id p, *pp;
+  Id *dp;
+  Id n;
+
+  queueinit(&q);
+  queuepush(&q, s - pool->solvables);/* push solvable Id */
+
+  while (q.count)
+    {
+      /*
+       * n: Id of solvable
+       * s: Pointer to solvable
+       */
+      
+      n = queueshift(&q);
+      if (MAPTST(m, n))                       /* continue if already set in map */
+       continue;
+
+      MAPSET(m, n);
+      s = pool->solvables + n;        /* s = Solvable in question */
+
+      dontfix = 0;
+      if (system                      /* have rpm */
+         && !solv->fixsystem
+         && n >= system->start        /* its an rpm rule */
+         && n < system->start + system->nsolvables)
+      {
+       dontfix = 1;                   /* dont care about broken rpm deps */
+      }
+
+      /*-----------------------------------------
+       * check requires of s
+       */
+      
+      if ((reqp = s->requires) != ID_NULL)
+       {
+         while ((req = *reqp++) != ID_NULL)
+           {
+             if (req == SOLVABLE_PREREQMARKER)   /* skip the marker */
+               continue;
+
+             dp = GET_PROVIDESP(req, p);      /* get providers of req */
+
+             if (!*dp                       /* dont care if noone provides rpmlib() */
+                 && !strncmp(id2str(pool, req), "rpmlib(", 7))
+               {
+                 continue;
+               }
+
+             if (dontfix)             /* rpm rule, dont care about breakage */
+               {
+                 for (i = 0; dp[i]; i++)/* for all providers */
+                   {
+                     if (dp[i] >= system->start && dp[i] < system->start + system->nsolvables)
+                       break;         /* provider is installed */
+                   }
+                 if (!dp[i])          /* no provider found */
+                   {
+                     if (pool->verbose) printf("ignoring broken requires %s%s%s of system package %s-%s.%s\n", id2str(pool, req), id2rel(pool, req), id2evr(pool, req), id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+                     continue;
+                   }
+               }
+
+             if (!*dp)
+               {
+                 /* nothing provides req! */
+  #if 1
+                 if (pool->verbose) printf("package %s-%s.%s is not installable (%s%s%s)\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch), id2str(pool, req), id2rel(pool, req), id2evr(pool, req));
+  #endif
+                 addrule(solv, -n, 0); /* mark requestor as uninstallable */
+                 if (solv->rc_output)
+                   printf(">!> !unflag %s-%s.%s[%s]\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch), source_name(pool_source(pool, s)));
+                 continue;
+               }
+  #if 0
+             printf("addrule %s-%s.%s %s%s%s %d %d\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch), id2str(pool, req), id2rel(pool, req), id2evr(pool, req), -n, dp - pool->whatprovidesdata);
+             for (i = 0; dp[i]; i++)
+               printf("  %s-%s.%s\n", id2str(pool, pool->solvables[dp[i]].name), id2str(pool, pool->solvables[dp[i]].evr), id2str(pool, pool->solvables[dp[i]].arch));
+  #endif
+             /* add 'requires' dependency */
+             addrule(solv, -n, dp - pool->whatprovidesdata);   /* rule: (-requestor|provider1|provider2|...|providerN) */
+
+             /* descend the dependency tree */
+             for (; *dp != ID_NULL; dp++)   /* loop through all providers */
+               {
+                 if (!MAPTST(m, *dp))     /* if not already marked */
+                   queuepush(&q, *dp);    /* queue for installation */
+               }
+
+           } /* while, requirements of n */
+
+       } /* if, requirements */
+
+      
+      /*-----------------------------------------
+       * check conflicts of s
+       */
+      
+      if ((conp = s->conflicts) != ID_NULL)
+       {
+         while ((con = *conp++) != ID_NULL)
+           {
+             FOR_PROVIDES(p, pp, con)   /* loop through all providers of this conflict */
+               {
+                                          /* dontfix: dont care about conflicts with already installed packs */
+                 if (dontfix && p >= system->start && p < system->start + system->nsolvables)
+                   continue;
+                 addrule(solv, -n, -p);   /* rule: -n|-p: either solvable _or_ provider of conflict */
+               }
+           }
+       }
+
+      /*-----------------------------------------
+       * check obsoletes if not installed
+       */
+      if (!system || n < system->start || n >= (system->start + system->nsolvables))
+       {                              /* not installed */
+         if ((obsp = s->obsoletes) != ID_NULL)
+           {
+             while ((obs = *obsp++) != ID_NULL)
+               {
+                 FOR_PROVIDES(p, pp, obs)
+                   addrule(solv, -n, -p);
+               }
+           }
+         FOR_PROVIDES(p, pp, s->name)
+           {
+             if (s->name == pool->solvables[p].name)
+               addrule(solv, -n, -p);
+           }
+       }
+
+      /*-----------------------------------------
+       * add recommends to the rule list
+       */
+      if ((recp = s->recommends) != ID_NULL)
+       while ((rec = *recp++) != ID_NULL)
+         {
+           FOR_PROVIDES(p, pp, rec)
+             if (!MAPTST(m, p))
+               queuepush(&q, p);
+         }
+    }
+  queuefree(&q);
+}
+
+static void
+addrulesforsupplements(Solver *solv, Map *m)
+{
+  Pool *pool = solv->pool;
+  Solvable *s;
+  Id sup, *supp;
+  Id p, *pp;
+  int i, n;
+
+  if (pool->verbose) printf("addrulesforsupplements... (%d)\n", solv->nrules);
+  for (i = n = 1; n < pool->nsolvables; i++, n++)
+    {
+      if (i == pool->nsolvables)
+       i = 1;
+      if (MAPTST(m, i))
+       continue;
+      s = pool->solvables + i;
+      if (!(supp = s->supplements))
+       continue;
+      while ((sup = *supp++) != ID_NULL)
+       {
+         FOR_PROVIDES(p, pp, sup)
+           if (MAPTST(m, p))
+             break;
+         if (p)
+           break;
+       }
+      if (!sup)
+       continue;
+      addrulesforsolvable(solv, s, m);
+      n = 0;
+    }
+  if (pool->verbose) printf("done. (%d)\n", solv->nrules);
+}
+
+
+static inline int
+archchanges(Pool *pool, Solvable *s1, Solvable *s2)
+{
+  Id a1 = s1->arch, a2 = s2->arch;
+
+  /* we allow changes to/from noarch */
+  if (a1 == a2 || a1 == ARCH_NOARCH || a2 == ARCH_NOARCH)
+    return 0;
+  if (!pool->id2arch)
+    return 0;
+  a1 = a1 <= pool->lastarch ? pool->id2arch[a1] : 0;
+  a2 = a2 <= pool->lastarch ? pool->id2arch[a2] : 0;
+  if (((a1 ^ a2) & 0xffff0000) != 0)
+    return 1;
+  return 0;
+}
+
+
+static void
+findupdatepackages(Solver *solv, Solvable *s, Queue *qs, Map *m, int allowdowngrade, int allowarchchange)
+{
+  /* system packages get a special upgrade allowed rule */
+  Pool *pool = solv->pool;
+  Id p, *pp, n, p2, *pp2;
+  Id obs, *obsp;
+
+  QUEUEEMPTY(qs);
+  /*
+   * s = solvable ptr
+   * n = solvable Id
+   */
+  n = s - pool->solvables;
+  if (m && !MAPTST(m, n))      /* add rule for s if not already done */
+    addrulesforsolvable(solv, s, m);
+
+  /*
+   * look for updates for s
+   */
+  FOR_PROVIDES(p, pp, s->name) /* every provider of s' name */
+    {
+      if (p == n)              /* skip itself */
+       continue;
+
+      if (s->name == pool->solvables[p].name)  /* name match */
+       {
+         if (!allowdowngrade                   /* consider downgrades ? */
+             && evrcmp(pool, s->evr, pool->solvables[p].evr) > 0)
+           continue;
+         /* XXX */
+         if (!allowarchchange && archchanges(pool, s, pool->solvables + p))
+           continue;
+       }
+      else if ((obsp = pool->solvables[p].obsoletes) != 0)   /* provides/obsoletes combination ? */
+       {
+         while ((obs = *obsp++) != 0)  /* for all obsoletes */
+           {
+             FOR_PROVIDES(p2, pp2, obs)   /* and all matching providers of the obsoletes */
+             {
+               if (p2 == n)            /* match ! */
+                 break;
+             }
+             if (p2)                   /* match! */
+               break;
+           }
+         if (!obs)                     /* continue if no match */
+           continue;
+         /* here we have 'p' with a matching provides/obsoletes combination
+          * thus flagging p as a valid update candidate for s
+          */
+       }
+      queuepush(qs, p);
+
+      if (m && !MAPTST(m, p))          /* mark p for install if not already done */
+       addrulesforsolvable(solv, pool->solvables + p, m);
+    }
+}
+
+/*
+ * add rule for update
+ *   (A|A1|A2|A3...)  An = update candidates for A
+ * 
+ * s = (installed) solvable
+ * m = 'addedmap', bit set if 'install' rule for solvable exists
+ */
+
+static void
+addupdaterule(Solver *solv, Solvable *s, Map *m, int allowdowngrade, int allowarchchange, int dontaddrule)
+{
+  /* system packages get a special upgrade allowed rule */
+  Pool *pool = solv->pool;
+  Id d, n;
+  Rule *r;
+  Queue qs;
+
+  queueinit(&qs);
+  findupdatepackages(solv, s, &qs, m, allowdowngrade, allowarchchange);
+  n = s - pool->solvables;
+  if (dontaddrule)     /* we consider update candidates but dont force them */
+    {
+      queuefree(&qs);
+      return;
+    }
+
+  if (qs.count == 0)                  /* no updates found */
+    {
+#if 0
+      printf("new update rule: must keep %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+#endif
+      addrule(solv, n, 0);             /* request 'install' of s */
+      queuefree(&qs);
+      return;
+    }
+
+  d = pool_queuetowhatprovides(pool, &qs);   /* intern computed provider queue */
+  queuefree(&qs);
+  r = addrule(solv, n, d);            /* allow update of s */
+#if 0
+  printf("new update rule ");
+  if (r)
+    printrule(solv, r);
+#endif
+}
+
+
+/*-----------------------------------------------------------------*/
+/* watches */
+
+
+/*
+ * makewatches
+ * 
+ * initial setup for all watches
+ */
+
+static void
+makewatches(Solver *solv)
+{
+  Rule *r;
+  int i;
+  int nsolvables = solv->pool->nsolvables;
+
+  xfree(solv->watches);
+                                      /* lower half for removals, upper half for installs */
+  solv->watches = (Id *)xcalloc(2 * nsolvables, sizeof(Id));
+#if 1
+  /* do it reverse so rpm rules get triggered first */
+  for (i = 1, r = solv->rules + solv->nrules - 1; i < solv->nrules; i++, r--)
+#else
+  for (i = 1, r = solv->rules + 1; i < solv->nrules; i++, r++)
+#endif
+    {
+      if (!r->w1                      /* rule is disabled */
+         || !r->w2)                   /* rule is assertion */
+       continue;
+
+      /* see addwatches(solv, r) */
+      r->n1 = solv->watches[nsolvables + r->w1];
+      solv->watches[nsolvables + r->w1] = r - solv->rules;
+
+      r->n2 = solv->watches[nsolvables + r->w2];
+      solv->watches[nsolvables + r->w2] = r - solv->rules;
+    }
+}
+
+
+/*
+ * add watches (for rule)
+ */
+
+static void
+addwatches(Solver *solv, Rule *r)
+{
+  int nsolvables = solv->pool->nsolvables;
+
+  r->n1 = solv->watches[nsolvables + r->w1];
+  solv->watches[nsolvables + r->w1] = r - solv->rules;
+
+  r->n2 = solv->watches[nsolvables + r->w2];
+  solv->watches[nsolvables + r->w2] = r - solv->rules;
+}
+
+
+/*-----------------------------------------------------------------*/
+/* rule propagation */
+
+#define DECISIONMAP_TRUE(p) ((p) > 0 ? (decisionmap[p] > 0) : (decisionmap[-p] < 0))
+
+/*
+ * propagate
+ * 
+ * propagate decision to all rules
+ */
+
+static Rule *
+propagate(Solver *solv, int level)
+{
+  Pool *pool = solv->pool;
+  Id *rp, *nrp;
+  Rule *r;
+  Id p, pkg, ow;
+  Id *dp;
+  Id *decisionmap = solv->decisionmap;
+  Id *watches = solv->watches + pool->nsolvables;
+
+  while (solv->propagate_index < solv->decisionq.count)
+    {
+      /* negative because our watches trigger if literal goes FALSE */
+      pkg = -solv->decisionq.elements[solv->propagate_index++];
+#if 0
+  printf("popagate for decision %d level %d\n", -pkg, level);
+  printruleelement(solv, 0, -pkg);
+#endif
+      for (rp = watches + pkg; *rp; rp = nrp)
+       {
+         r = solv->rules + *rp;
+#if 0
+  printf("  watch triggered ");
+  printrule(solv, r);
+#endif
+         if (pkg == r->w1)
+           {
+             ow = r->w2;
+             nrp = &r->n1;
+           }
+         else
+           {
+             ow = r->w1;
+             nrp = &r->n2;
+           }
+         /* if clause is TRUE, nothing to do */
+         if (DECISIONMAP_TRUE(ow))
+           continue;
+
+          if (r->d)
+           {
+             /* not a binary clause, check if we need to move our watch */
+             if (r->p && r->p != ow && !DECISIONMAP_TRUE(-r->p))
+               p = r->p;
+             else
+               for (dp = pool->whatprovidesdata + r->d; (p = *dp++) != 0;)
+                 if (p != ow && !DECISIONMAP_TRUE(-p))
+                   break;
+             if (p)
+               {
+                 /* p is free to watch, move watch to p */
+#if 0
+                 if (p > 0)
+                   printf("    -> move w%d to %s-%s.%s\n", (pkg == r->w1 ? 1 : 2), id2str(pool, pool->solvables[p].name), id2str(pool, pool->solvables[p].evr), id2str(pool, pool->solvables[p].arch));
+                 else
+                   printf("    -> move w%d to !%s-%s.%s\n", (pkg == r->w1 ? 1 : 2), id2str(pool, pool->solvables[-p].name), id2str(pool, pool->solvables[-p].evr), id2str(pool, pool->solvables[-p].arch));
+#endif
+                 *rp = *nrp;
+                 nrp = rp;
+                 if (pkg == r->w1)
+                   {
+                     r->w1 = p;
+                     r->n1 = watches[p];
+                   }
+                 else
+                   {
+                     r->w2 = p;
+                     r->n2 = watches[p];
+                   }
+                 watches[p] = r - solv->rules;
+                 continue;
+               }
+           }
+          /* unit clause found, set other watch to TRUE */
+         if (DECISIONMAP_TRUE(-ow))
+           return r;           /* eek, a conflict! */
+#if 0
+         printf("unit ");
+         printrule(solv, r);
+#endif
+         if (ow > 0)
+            decisionmap[ow] = level;
+         else
+            decisionmap[-ow] = -level;
+         queuepush(&solv->decisionq, ow);
+         queuepush(&solv->decisionq_why, r - solv->rules);
+#if 0
+           {
+             Solvable *s = pool->solvables + (ow > 0 ? ow : -ow);
+             if (ow > 0)
+               printf("  -> decided to install %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+             else
+               printf("  -> decided to conflict %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+           }
+#endif
+       }
+    }
+  return 0;    /* all is well */
+}
+
+
+/*-----------------------------------------------------------------*/
+/* Analysis */
+
+/*
+ * analyze
+ *   and learn
+ */
+
+static int
+analyze(Solver *solv, int level, Rule *c, int *pr, int *dr, int *why)
+{
+  Pool *pool = solv->pool;
+  Queue r;
+  int rlevel = 1;
+  Map seen;            /* global? */
+  Id v, vv, *dp;
+  int l, i, idx;
+  int num = 0;
+  int learnt_why = solv->learnt_pool.count;
+  Id *decisionmap = solv->decisionmap;
+  queueinit(&r);
+
+  if (pool->verbose) printf("ANALYZE at %d ----------------------\n", level);
+  mapinit(&seen, pool->nsolvables);
+  idx = solv->decisionq.count;
+  for (;;)
+    {
+      printrule(solv, c);
+      queuepush(&solv->learnt_pool, c - solv->rules);
+      dp = c->d ? pool->whatprovidesdata + c->d : 0;
+      for (i = -1; ; i++)
+       {
+         if (i == -1)
+           v = c->p;
+         else if (c->d == 0)
+           v = i ? 0 : c->w2;
+         else
+           v = *dp++;
+         if (v == 0)
+           break;
+         if (DECISIONMAP_TRUE(v))      /* the one true literal */
+             continue;
+         vv = v > 0 ? v : -v;
+         if (MAPTST(&seen, vv))
+           continue;
+         l = solv->decisionmap[vv];
+         if (l < 0)
+           l = -l;
+         if (l == 1)
+           {
+#if 0
+             int j;
+             for (j = 0; j < solv->decisionq.count; j++)
+               if (solv->decisionq.elements[j] == v)
+                 break;
+             if (j == solv->decisionq.count)
+               abort();
+             queuepush(&rulq, -(j + 1));
+#endif
+             continue;                 /* initial setting */
+           }
+         MAPSET(&seen, vv);
+         if (l == level)
+           num++;                      /* need to do this one as well */
+         else
+           {
+             queuepush(&r, v);
+#if 0
+  printf("PUSH %d ", v);
+  printruleelement(solv, 0, v);
+#endif
+             if (l > rlevel)
+               rlevel = l;
+           }
+       }
+#if 0
+      printf("num = %d\n", num);
+#endif
+      if (num <= 0)
+       abort();
+      for (;;)
+       {
+         v = solv->decisionq.elements[--idx];
+         vv = v > 0 ? v : -v;
+         if (MAPTST(&seen, vv))
+           break;
+       }
+      c = solv->rules + solv->decisionq_why.elements[idx];
+      MAPCLR(&seen, vv);
+      if (--num == 0)
+       break;
+    }
+  *pr = -v;
+  if (r.count == 0)
+    *dr = 0;
+  else if (r.count == 1 && r.elements[0] < 0)
+    *dr = r.elements[0];
+  else
+    *dr = pool_queuetowhatprovides(pool, &r);
+  if (pool->verbose)
+    {
+      printf("learned rule for level %d (am %d)\n", rlevel, level);
+      printruleelement(solv, 0, -v);
+      for (i = 0; i < r.count; i++)
+        {
+          v = r.elements[i];
+          printruleelement(solv, 0, v);
+        }
+    }
+  mapfree(&seen);
+  queuepush(&solv->learnt_pool, 0);
+#if 0
+  for (i = learnt_why; solv->learnt_pool.elements[i]; i++)
+    {
+      printf("learnt_why ");
+      printrule(solv, solv->rules + solv->learnt_pool.elements[i]);
+    }
+#endif
+  if (why)
+    *why = learnt_why;
+  return rlevel;
+}
+
+
+/*
+ * reset_solver
+ * reset the solver decisions to right after the rpm rules
+ */
+
+static void
+reset_solver(Solver *solv)
+{
+  int i;
+  Id v;
+  Rule *r;
+
+  /* delete all learnt rules */
+  solv->nrules = solv->learntrules;
+  QUEUEEMPTY(&solv->learnt_why);
+  QUEUEEMPTY(&solv->learnt_pool);
+
+  /* redo all direct decision without the disabled rules */
+  for (i = 0; i < solv->decisionq.count; i++)
+    {
+      v = solv->decisionq.elements[i];
+      solv->decisionmap[v > 0 ? v : -v] = 0;
+    }
+  for (i = 0; i < solv->decisionq_why.count; i++)
+    if (solv->decisionq_why.elements[i])
+      break;
+    else
+      {
+        v = solv->decisionq.elements[i];
+        solv->decisionmap[v > 0 ? v : -v] = v > 0 ? 1 : -1;
+      }
+
+  if (solv->pool->verbose)
+    printf("decisions done reduced from %d to %d\n", solv->decisionq.count, i);
+
+  solv->decisionq_why.count = i;
+  solv->decisionq.count = i;
+  if (i < solv->propagate_index)
+    solv->propagate_index = i;
+  /* make direct decisions from enabled unary rules */
+  for (i = solv->jobrules, r = solv->rules + solv->jobrules; i < solv->nrules; i++, r++)
+    {
+      if (!r->w1 || r->w2)
+       continue;
+#if 0
+      printrule(solv, r);
+#endif
+      v = r->p;
+      queuepush(&solv->decisionq, v);
+      queuepush(&solv->decisionq_why, r - solv->rules);
+      solv->decisionmap[v > 0 ? v : -v] = v > 0 ? 1 : -1;
+    }
+  if (solv->pool->verbose)
+    printf("decisions after adding job and system rules: %d\n", solv->decisionq.count);
+  /* recreate watches */
+  makewatches(solv);
+}
+
+
+/*
+ * analyze_unsolvable_rule
+ */
+
+static void
+analyze_unsolvable_rule(Solver *solv, Rule *c, int disablerules)
+{
+  Id why;
+  int i;
+
+  why = c - solv->rules;
+#if 0
+  if (why >= solv->jobrules && why < solv->systemrules)
+    printf("JOB ");
+  if (why >= solv->systemrules && why < solv->learntrules)
+    printf("SYSTEM %d ", why - solv->systemrules);
+  if (solv->learntrules && why >= solv->learntrules)
+    printf("LEARNED ");
+  printrule(solv, c);
+#endif
+  if (solv->learntrules && why >= solv->learntrules)
+    {
+      for (i = solv->learnt_why.elements[why - solv->learntrules]; solv->learnt_pool.elements[i]; i++)
+       analyze_unsolvable_rule(solv, solv->rules + solv->learnt_pool.elements[i], disablerules);
+      return;
+    }
+  if (why >= solv->jobrules && why < solv->learntrules)
+    {
+      if (disablerules)
+       {
+         /* turn off rule for further analysis */
+         c->w1 = 0;
+       }
+      /* unify problem */
+      if (solv->problems.count)
+       {
+         for (i = solv->problems.count - 1; i >= 0; i--)
+           if (solv->problems.elements[i] == 0)
+             break;
+           else if (solv->problems.elements[i] == why)
+             return;
+       }
+      queuepush(&solv->problems, why);
+    }
+}
+
+
+/*
+ * analyze_unsolvable
+ */
+
+static void
+analyze_unsolvable(Solver *solv, Rule *c, int disablerules)
+{
+  Pool *pool = solv->pool;
+  Map seen;            /* global? */
+  Id v, vv, *dp, why;
+  int l, i, idx;
+  Id *decisionmap = solv->decisionmap;
+
+#if 0
+  printf("ANALYZE UNSOLVABLE ----------------------\n");
+#endif
+  mapinit(&seen, pool->nsolvables);
+  analyze_unsolvable_rule(solv, c, disablerules);
+  dp = c->d ? pool->whatprovidesdata + c->d : 0;
+  for (i = -1; ; i++)
+    {
+      if (i == -1)
+       v = c->p;
+      else if (c->d == 0)
+       v = i ? 0 : c->w2;
+      else
+       v = *dp++;
+      if (v == 0)
+       break;
+      if (DECISIONMAP_TRUE(v)) /* the one true literal */
+         continue;
+      vv = v > 0 ? v : -v;
+      l = solv->decisionmap[vv];
+      if (l < 0)
+       l = -l;
+      MAPSET(&seen, vv);
+    }
+  idx = solv->decisionq.count;
+  while (idx > 0)
+    {
+      v = solv->decisionq.elements[--idx];
+      vv = v > 0 ? v : -v;
+      if (!MAPTST(&seen, vv))
+       continue;
+      why = solv->decisionq_why.elements[idx];
+      if (!why)
+       {
+#if 0
+         printf("RPM ");
+         printruleelement(solv, 0, v);
+#endif
+         continue;
+       }
+      c = solv->rules + why;
+      analyze_unsolvable_rule(solv, c, disablerules);
+      dp = c->d ? pool->whatprovidesdata + c->d : 0;
+      for (i = -1; ; i++)
+       {
+         if (i == -1)
+           v = c->p;
+         else if (c->d == 0)
+           v = i ? 0 : c->w2;
+         else
+           v = *dp++;
+         if (v == 0)
+           break;
+         if (DECISIONMAP_TRUE(v))      /* the one true literal */
+             continue;
+         vv = v > 0 ? v : -v;
+         l = solv->decisionmap[vv];
+         if (l < 0)
+           l = -l;
+         MAPSET(&seen, vv);
+       }
+    }
+  mapfree(&seen);
+  queuepush(&solv->problems, 0);       /* mark end of this problem */
+  if (disablerules)
+    reset_solver(solv);
+#if 0
+  printf("analyze_unsolvables done\n");
+#endif
+}
+
+
+/*-----------------------------------------------------------------*/
+/* Decision revert */
+
+/*
+ * revert
+ * revert decision at level
+ */
+
+static void
+revert(Solver *solv, int level)
+{
+  Id v, vv;
+  while (solv->decisionq.count)
+    {
+      v = solv->decisionq.elements[solv->decisionq.count - 1];
+      vv = v > 0 ? v : -v;
+      if (solv->decisionmap[vv] <= level && solv->decisionmap[vv] >= -level)
+        break;
+#if 0
+      printf("reverting decision %d at %d\n", v, solv->decisionmap[vv]);
+#endif
+      solv->decisionmap[vv] = 0;
+      solv->decisionq.count--;
+      solv->decisionq_why.count--;
+      solv->propagate_index = solv->decisionq.count;
+    }
+}
+
+
+/*
+ * watch2onhighest
+ */
+
+static void
+watch2onhighest(Solver *solv, Rule *r)
+{
+  int l, wl = 0;
+  Id v, *dp;
+
+  if (!r->d)
+    return;    /* binary rule, both watches are set */
+  dp = solv->pool->whatprovidesdata + r->d;
+  while ((v = *dp++) != 0)
+    {
+      l = solv->decisionmap[v < 0 ? -v : v];
+      if (l < 0)
+       l = -l;
+      if (l > wl)
+       {
+         r->w2 = dp[-1];
+         wl = l;
+       }
+    }
+}
+
+
+/*
+ * setpropagatelearn
+ */
+
+static int
+setpropagatelearn(Solver *solv, int level, Id decision, int disablerules)
+{
+  Rule *r;
+  Id p, d;
+  int l, why;
+
+  if (decision)
+    {
+      level++;
+      if (decision > 0)
+        solv->decisionmap[decision] = level;
+      else
+        solv->decisionmap[-decision] = -level;
+      queuepush(&solv->decisionq, decision);
+      queuepush(&solv->decisionq_why, 0);
+    }
+  for (;;)
+    {
+      r = propagate(solv, level);
+      if (!r)
+       break;
+      if (level == 1)
+       {
+         analyze_unsolvable(solv, r, disablerules);
+         if (disablerules)
+           return 1;
+         return 0;
+       }
+      printf("conflict with rule #%d\n", (int)(r - solv->rules));
+      l = analyze(solv, level, r, &p, &d, &why);
+      if (l >= level || l <= 0)
+       abort();
+      printf("reverting decisions (level %d -> %d)\n", level, l);
+      level = l;
+      revert(solv, level);
+      r = addrule(solv, p, d);       /* p requires d */
+      if (!r)
+       abort();
+      if (solv->learnt_why.count != (r - solv->rules) - solv->learntrules)
+       {
+         printf("%d %d\n", solv->learnt_why.count, (int)(r - solv->rules) - solv->learntrules);
+         abort();
+       }
+      queuepush(&solv->learnt_why, why);
+      if (d)
+       {
+         /* at least 2 literals, needs watches */
+         watch2onhighest(solv, r);
+         addwatches(solv, r);
+       }
+      solv->decisionmap[p > 0 ? p : -p] = p > 0 ? level : -level;
+      queuepush(&solv->decisionq, p);
+      queuepush(&solv->decisionq_why, r - solv->rules);
+      printf("decision: ");
+      printruleelement(solv, 0, p);
+      printf("new rule: ");
+      printrule(solv, r);
+    }
+  return level;
+}
+
+/*-----------------------------------------------------------------*/
+/* Main solver interface */
+
+
+/*
+ * solver_create
+ * create solver structure
+ *
+ * pool: all available solvables
+ * system: installed Solvables
+ *
+ *
+ * Upon solving, rules are created to flag the Solvables
+ * of the 'system' Source as installed.
+ */
+
+Solver *
+solver_create(Pool *pool, Source *system)
+{
+  Solver *solv;
+  solv = (Solver *)xcalloc(1, sizeof(Solver));
+  solv->pool = pool;
+  solv->system = system;
+  pool->verbose = 1;
+
+  queueinit(&solv->decisionq);
+  queueinit(&solv->decisionq_why);
+  queueinit(&solv->problems);
+  queueinit(&solv->learnt_why);
+  queueinit(&solv->learnt_pool);
+
+  solv->decisionmap = (Id *)xcalloc(pool->nsolvables, sizeof(Id));
+  solv->rules = (Rule *)xmalloc((solv->nrules + (RULES_BLOCK + 1)) * sizeof(Rule));
+  memset(solv->rules, 0, sizeof(Rule));
+
+  solv->nrules = 1;
+
+  return solv;
+}
+
+
+/*
+ * solver_free
+ */
+
+void
+solver_free(Solver *solv)
+{
+  queuefree(&solv->decisionq);
+  queuefree(&solv->decisionq_why);
+  queuefree(&solv->learnt_why);
+  queuefree(&solv->learnt_pool);
+  xfree(solv->decisionmap);
+  xfree(solv->rules);
+  xfree(solv->watches);
+  xfree(solv->weaksystemrules);
+  xfree(solv);
+}
+
+
+/*
+ * reenablerule
+ * 
+ * r->w1 was set to 0, now find proper value for w1
+ */
+  
+static void
+reenablerule(Solver *solv, Rule *r)
+{
+  int i;
+  Id v, l, good;
+
+  if (!r->w2)                         /* not a rule, but an assertion */
+    {
+      r->w1 = r->p;
+      return;
+    }
+  if (!r->d)
+    {
+      if (r->w2 != r->p)
+       r->w1 = r->p;
+      else
+       r->w2 = r->d;                  /* mls: shouldn't this be r->w1 ? */
+      return;
+    }
+  good = 0;
+                                      /* put it on the first not-false literal */
+  for (i = -1; ; i++)
+    {
+      if (i == -1)
+       v = r->p;
+      else
+        v = solv->pool->whatprovidesdata[r->d + i];
+      if (!v)
+       {
+         printrule(solv, r);
+         abort();
+       }
+      if (v == r->w2)
+       continue;
+      l = solv->decisionmap[v > 0 ? v : -v];
+      if (!l || (v < 0 && l < 0) || (v > 0 && l > 0))
+       break;
+    }
+  r->w1 = v;
+}
+
+
+/*-------------------------------------------------------*/
+
+/*
+ * run_solver
+ * 
+ * all rules have been set up, not actually run the solver
+ *
+ */
+
+static void
+run_solver(Solver *solv, int disablerules, int doweak)
+{
+  Queue dq;
+  int systemlevel;
+  int level, olevel;
+  Rule *r;
+  int i, n;
+  Solvable *s;
+  Pool *pool = solv->pool;
+  Id p, *dp;
+
+#if 0
+  printf("number of rules: %d\n", solv->nrules);
+{
+  int i;
+  for (i = 0; i < solv->nrules; i++)
+    {
+      printrule(solv, solv->rules + i);
+    }
+}
+#endif
+
+  /* all new rules are learnt after this point */
+  solv->learntrules = solv->nrules;
+  /* crate watches lists */
+  makewatches(solv);
+
+  if (pool->verbose) printf("initial decisions: %d\n", solv->decisionq.count);
+
+  /* start SAT algorithm */
+  level = 1;
+  if (!solv->updatesystem)
+    systemlevel = 2;
+  else
+    systemlevel = 0;
+  if (pool->verbose) printf("solving...\n");
+
+  queueinit(&dq);
+  for (;;)
+    {
+      /*
+       * propagate
+       */
+      
+      if (level == 1)
+       {
+         if (pool->verbose) printf("propagating (%d %d)...\n", solv->propagate_index, solv->decisionq.count);
+         if ((r = propagate(solv, level)) != 0)
+           {
+             analyze_unsolvable(solv, r, disablerules);
+             if (disablerules)
+               continue;
+             printf("UNSOLVABLE\n");
+             queuefree(&dq);
+             return;
+           }
+       }
+
+      /*
+       * system packages
+       */
+      
+      if (level < systemlevel && solv->system->nsolvables)
+       {
+         if (pool->verbose) printf("installing system packages\n");
+         for (i = solv->system->start, n = 0; ; i++, n++)
+           {
+             if (n == solv->system->nsolvables)
+               break;
+             if (i == solv->system->start + solv->system->nsolvables)
+               i = solv->system->start;
+             s = pool->solvables + i;
+#if 0
+             if (solv->decisionmap[i] < 0)
+               {
+                 int j;
+                 printf("system %s-%s.%s conflicts\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+                 for (j = 0; j < solv->decisionq.count; j++)
+                   if (solv->decisionq.elements[j] == -i)
+                     break;
+                 if (solv->decisionq_why.elements[j])
+                   printrule(solv, solv->rules + solv->decisionq_why.elements[j]);
+               }
+#endif
+             if (solv->decisionmap[i] != 0)
+               continue;
+#if 0
+             printf("system installing %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+#endif
+             olevel = level;
+             level = setpropagatelearn(solv, level, i, disablerules);
+             if (level == 0)
+               {
+                 printf("UNSOLVABLE\n");
+                 queuefree(&dq);
+                 return;
+               }
+             if (level <= olevel)
+               n = 0;
+           }
+         if (solv->weaksystemrules)
+           {
+             if (pool->verbose) printf("installing weak system packages\n");
+             for (i = solv->system->start, n = 0; ; i++, n++)
+               {
+                 if (n == solv->system->nsolvables)
+                   break;
+                 if (solv->decisionmap[i] > 0 || solv->weaksystemrules[i - solv->system->start] == 0)
+                   continue;
+                 QUEUEEMPTY(&dq);
+                 dp = pool->whatprovidesdata + solv->weaksystemrules[i - solv->system->start];
+                 while ((p = *dp++) != 0)
+                   {
+                     if (solv->decisionmap[p] > 0)
+                       break;
+                     if (solv->decisionmap[p] == 0)
+                       queuepush(&dq, p);
+                   }
+                 if (p || !dq.count)
+                   continue;
+
+
+                 if (dq.count > 1)
+                   prune_best_version_arch(pool, &dq);
+#if 0
+                 s = pool->solvables + dq.elements[0];
+                 printf("weak system installing %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+#endif
+                 olevel = level;
+                 level = setpropagatelearn(solv, level, dq.elements[0], disablerules);
+                 if (level == 0)
+                   {
+                     printf("UNSOLVABLE\n");
+                     queuefree(&dq);
+                     return;
+                   }
+                 if (level <= olevel)
+                   {
+                     n = 0;
+                     break;
+                   }
+               }
+             if (n != solv->system->nsolvables)
+               continue;
+           }
+         systemlevel = level;
+       }
+
+      /*
+       * decide
+       */
+      
+      if (pool->verbose) printf("deciding unresolved rules\n");
+      for (i = 1, n = 1; ; i++, n++)
+       {
+         if (n == solv->nrules)
+           break;
+         if (i == solv->nrules)
+           i = 1;
+         r = solv->rules + i;
+         if (!r->w1)
+           continue;
+         QUEUEEMPTY(&dq);
+         if (r->d == 0)
+           {
+             /* binary or unary rule */
+             /* need two positive undecided literals */
+             if (r->p < 0 || r->w2 <= 0)
+               continue;
+             if (solv->decisionmap[r->p] || solv->decisionmap[r->w2])
+               continue;
+             queuepush(&dq, r->p);
+             queuepush(&dq, r->w2);
+           }
+         else
+           {
+             /* make sure that
+               * all negative literals are installed
+               * no positive literal is installed
+              * i.e. the rule is not fulfilled and we
+               * just need to decide on the positive literals
+               */
+             if (r->p < 0)
+               {
+                 if (solv->decisionmap[-r->p] <= 0)
+                   continue;
+               }
+             else
+               {
+                 if (solv->decisionmap[r->p] > 0)
+                   continue;
+                 if (solv->decisionmap[r->p] == 0)
+                   queuepush(&dq, r->p);
+               }
+             dp = pool->whatprovidesdata + r->d;
+             while ((p = *dp++) != 0)
+               {
+                 if (p < 0)
+                   {
+                     if (solv->decisionmap[-p] <= 0)
+                       break;
+                   }
+                 else
+                   {
+                     if (solv->decisionmap[p] > 0)
+                       break;
+                     if (solv->decisionmap[p] == 0)
+                       queuepush(&dq, p);
+                   }
+               }
+             if (p)
+               continue;
+           }
+         if (dq.count < 2)
+           {
+             /* cannot happen as this means that
+               * the rule is unit */
+             printrule(solv, r);
+             abort();
+           }
+         prune_best_version_arch(pool, &dq);
+         p = dq.elements[dq.count - 1];
+         s = pool->solvables + p;
+#if 0
+         printf("installing %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+#endif
+         olevel = level;
+         level = setpropagatelearn(solv, level, p, disablerules);
+         if (level == 0)
+           {
+             printf("UNSOLVABLE\n");
+             queuefree(&dq);
+             return;
+           }
+         if (level < systemlevel)
+           break;
+         if (level <= olevel)
+           n = 0;
+       } /* for(), decide */
+
+      /*
+       * check for end
+       */
+      
+      if (n != solv->nrules)
+       continue;
+      if (doweak && !solv->problems.count)
+       {
+         int qcount;
+
+         if (pool->verbose) printf("installing recommended packages\n");
+         QUEUEEMPTY(&dq);
+         for (i = 1; i < pool->nsolvables; i++)
+           {
+             if (solv->decisionmap[i] < 0)
+               continue;
+             if (solv->decisionmap[i] > 0)
+               {
+                 Id *recp, rec, *pp, p;
+                 s = pool->solvables + i;
+                 /* installed, check for recommends */
+                 if ((recp = s->recommends) != 0)
+                   {
+                     while ((rec = *recp++) != 0)
+                       {
+                         qcount = dq.count;
+                         FOR_PROVIDES(p, pp, rec)
+                           {
+                             if (solv->decisionmap[p] > 0)
+                               break;
+                             else if (solv->decisionmap[p] == 0)
+                               queuepushunique(&dq, p);
+                           }
+                         if (p)
+                           dq.count = qcount;  /* already fulfilled */
+                       }
+                   }
+               }
+             else
+               {
+                 Id *supp, sup, *pp, p;
+                 s = pool->solvables + i;
+                 if ((supp = s->supplements) != 0)
+                   {
+                     while ((sup = *supp++) != 0)
+                       {
+                         FOR_PROVIDES(p, pp, sup)
+                           {
+                             if (solv->decisionmap[p] > 0)
+                               break;
+                           }
+                         if (p)
+                           queuepushunique(&dq, i);
+                       }
+                   }
+               }
+           }
+         if (dq.count)
+           {
+             prune_best_version_arch(pool, &dq);
+             p = dq.elements[dq.count - 1];
+             s = pool->solvables + p;
+#if 0
+             printf("weak installing %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+#endif
+             level = setpropagatelearn(solv, level, p, 0);
+             continue;
+           }
+       }
+      break;
+    }
+  queuefree(&dq);
+}
+
+  
+/*
+ * refine_suggestion
+ */
+  
+static void
+refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined)
+{
+  Rule *r;
+  int i, j;
+  Id v;
+  Queue disabled;
+  int disabledcnt;
+
+  printf("refine_suggestion start\n");
+  queueinit(&disabled);
+  QUEUEEMPTY(refined);
+  queuepush(refined, sug);
+
+  /* re-enable all rules but rule "sug" of the problem */
+  for (i = 0; problem[i]; i++)
+    {
+      if (problem[i] == sug)
+       continue;
+      r = solv->rules + problem[i];
+#if 0
+      printf("enable ");
+      printrule(solv, r);
+#endif
+      reenablerule(solv, r);
+    }
+  for (;;)
+    {
+      revert(solv, 1);
+      reset_solver(solv);
+      QUEUEEMPTY(&solv->problems);
+      run_solver(solv, 0, 0);
+      if (!solv->problems.count)
+       {
+         printf("no more problems!\n");
+#if 0
+         printdecisions(solv);
+#endif
+         break;                /* great, no more problems */
+       }
+      disabledcnt = disabled.count;
+      for (i = 0; i < solv->problems.elements[i]; i++)
+       {
+         /* ignore solutions in refined */
+          v = solv->problems.elements[i];
+         for (j = 0; problem[j]; j++)
+           if (problem[j] != sug && problem[j] == v)
+             break;
+         if (problem[j])
+           continue;
+         queuepush(&disabled, v);
+       }
+      if (disabled.count == disabledcnt)
+       {
+         /* no solution found, this was an invalid suggestion! */
+         printf("no solution found!\n");
+         for (i = 0; i < refined->count; i++)
+           reenablerule(solv, solv->rules + refined->elements[i]);
+         refined->count = 0;
+         break;
+       }
+      if (disabled.count == disabledcnt + 1)
+       {
+         /* just one solution, add it to refined list */
+         queuepush(refined, disabled.elements[disabledcnt]);
+       }
+      else
+       {
+         printf("##############################################   more than one solution found.\n");
+#if 1
+         for (i = 0; i < solv->problems.elements[i]; i++)
+           {
+             printrule(solv, solv->rules + solv->problems.elements[i]);
+           }
+         printf("##############################################\n");
+#endif
+         /* more than one solution */
+         /* for now return */
+       }
+      for (i = disabledcnt; i < disabled.count; i++)
+       {
+         r = solv->rules + disabled.elements[i];;
+         /* disable it */
+         r->w1 = 0;
+#if 0
+         printf("disable ");
+         printrule(solv, r);
+#endif
+       }
+    }
+  /* enable refined rules again */
+  reset_solver(solv);
+  for (i = 0; i < disabled.count; i++)
+    reenablerule(solv, solv->rules + disabled.elements[i]);
+  /* disable problem rules again so that we are in the same state as before */
+  for (i = 0; problem[i]; i++)
+    {
+      r = solv->rules + problem[i];
+      r->w1 = 0;
+    }
+  printf("refine_suggestion end\n");
+}
+
+  
+/*
+ * printdecisions
+ */
+  
+static const char *
+id2rc(Solver *solv, Id id)
+{
+  const char *evr;
+  if (solv->rc_output != 2)
+    return "";
+  evr = id2str(solv->pool, id);
+  if (*evr < '0' || *evr > '9')
+    return "0:";
+  while (*evr >= '0' && *evr <= '9')
+    evr++;
+  if (*evr != ':')
+    return "0:";
+  return "";
+}
+
+void
+printdecisions(Solver *solv)
+{
+  Pool *pool = solv->pool;
+  Id p, *obsoletesmap;
+  int i;
+  Solvable *s;
+
+  obsoletesmap = (Id *)xcalloc(pool->nsolvables, sizeof(Id));
+  for (i = 0; i < solv->decisionq.count; i++)
+    {
+      Id obs, *obsp;
+      Id *pp, n;
+
+      n = solv->decisionq.elements[i];
+      if (n < 0)
+       continue;
+      if (n >= solv->system->start && n < solv->system->start + solv->system->nsolvables)
+       continue;
+      s = pool->solvables + n;
+      if ((obsp = s->obsoletes) != 0)
+       while ((obs = *obsp++) != 0)
+         FOR_PROVIDES(p, pp, obs)
+           {
+             if (p >= solv->system->start && p < solv->system->start + solv->system->nsolvables)
+               {
+                 obsoletesmap[p] = n;
+                 obsoletesmap[n]++;
+               }
+           }
+      FOR_PROVIDES(p, pp, s->name)
+       if (s->name == pool->solvables[p].name)
+         {
+           if (p >= solv->system->start && p < solv->system->start + solv->system->nsolvables)
+             {
+               obsoletesmap[p] = n;
+               obsoletesmap[n]++;
+             }
+         }
+    }
+
+  if (solv->rc_output)
+    printf(">!> Solution #1:\n");
+
+  int installs = 0, uninstalls = 0, upgrades = 0;
+  
+  /* print solvables to be erased */
+
+  for (i = solv->system->start; i < solv->system->start + solv->system->nsolvables; i++)
+    {
+      if (solv->decisionmap[i] > 0)
+       continue;
+      if (obsoletesmap[i])
+       continue;
+      s = pool->solvables + i;
+      if (solv->rc_output)
+       {
+         printf(">!> ");
+         if (solv->rc_output == 2)
+           {
+             printf("remove ");
+             printf(" %s-%s%s", id2str(pool, s->name), id2rc(solv, s->evr), id2str(pool, s->evr));
+           }
+         else
+           {
+             printf("remove  %s-%s.%s", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+           }
+       }
+      else
+        {
+          printf("erase   %s-%s.%s", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+        }
+      printf("\n");
+      uninstalls++;
+    }
+
+  /* print solvables to be installed */
+
+  for (i = 0; i < solv->decisionq.count; i++)
+    {
+      p = solv->decisionq.elements[i];
+      if (p < 0)
+       continue;
+      if (p >= solv->system->start && p < solv->system->start + solv->system->nsolvables)
+       continue;
+      s = pool->solvables + p;
+
+      if (solv->rc_output)
+       printf(">!> ");
+
+      if (!obsoletesmap[p])
+        {
+          printf("install %s-%s%s", id2str(pool, s->name), id2rc(solv, s->evr), id2str(pool, s->evr));
+         if (solv->rc_output != 2)
+            printf(".%s", id2str(pool, s->arch));
+         installs++;
+        }
+      else
+       {
+         int j;
+         Solvable *from = NULL, *to = NULL;
+         if (solv->rc_output)
+           to = s;
+         else
+           printf("update  %s-%s.%s  (obsoletes", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+         upgrades++;
+         for (j = solv->system->start; j < solv->system->start + solv->system->nsolvables; j++)
+           {
+             if (obsoletesmap[j] == p)
+               {
+                 s = pool->solvables + j;
+                 if (solv->rc_output)
+                   from = s;
+                 else
+                   printf(" %s-%s.%s", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+               }
+           }
+         if (solv->rc_output)
+           {
+             if (solv->rc_output == 2)
+               printf("upgrade %s-%s => %s-%s%s", id2str(pool, from->name), id2str(pool, from->evr), id2str(pool, to->name), id2rc(solv, to->evr), id2str(pool, to->evr));
+             else
+               printf("upgrade %s-%s.%s => %s-%s.%s", id2str(pool, from->name), id2str(pool, from->evr), id2str(pool, from->arch), id2str(pool, to->name), id2str(pool, to->evr), id2str(pool, to->arch));
+             s = to;                  /* for final source name */
+           }
+         else
+           printf(")");
+       }
+      if (solv->rc_output)
+       {
+         Source *source = pool_source(pool, s);
+         if (source)
+           printf("[%s]", source_name(source));
+        }
+      printf("\n");
+    }
+
+  if (solv->rc_output)
+    printf(">!> installs=%d, upgrades=%d, uninstalls=%d\n", installs, upgrades, uninstalls);
+  
+  xfree(obsoletesmap);
+}
+
+
+/*-----------------------------------------------------------------*/
+/* main() */
+
+/*
+ *
+ * solve job queue
+ *
+ */
+
+void
+solve(Solver *solv, Queue *job)
+{
+  Pool *pool = solv->pool;
+  int i;
+  Map addedmap;                               /* '1' == have rule for solvable */
+  Map noupdaterule;                   /* '1' == don't update (scheduled for removal) */
+  Id how, what, p, *pp, d;
+  Queue q;
+  Rule *r;
+  Solvable *s;
+
+  /*
+   * create basic rule set of all involved packages
+   * as bitmaps
+   * 
+   */
+
+  mapinit(&addedmap, pool->nsolvables);
+  mapinit(&noupdaterule, pool->nsolvables);
+
+  queueinit(&q);
+
+  /*
+   * create rules for installed solvables -> keep them installed
+   * so called: rpm rules
+   * 
+   */
+
+  for (i = solv->system->start; i < solv->system->start + solv->system->nsolvables; i++)
+    addrulesforsolvable(solv, pool->solvables + i, &addedmap);
+
+  /*
+   * create install rules
+   * 
+   * two passes, as we want to keep the rpm rules distinct from the job rules
+   * 
+   */
+
+  /*
+   * solvable rules
+   *  process job rules for solvables
+   */
+  
+  for (i = 0; i < job->count; i += 2)
+    {
+      how = job->elements[i];
+      what = job->elements[i + 1];
+
+      switch(how)
+       {
+       case SOLVER_INSTALL_SOLVABLE:
+         addrulesforsolvable(solv, pool->solvables + what, &addedmap);
+         break;
+       case SOLVER_INSTALL_SOLVABLE_NAME:
+       case SOLVER_INSTALL_SOLVABLE_PROVIDES:
+         QUEUEEMPTY(&q);
+         FOR_PROVIDES(p, pp, what)
+           {
+                                      /* if by name, ensure that the name matches */
+             if (how == SOLVER_INSTALL_SOLVABLE_NAME && pool->solvables[p].name != what)
+               continue;
+             addrulesforsolvable(solv, pool->solvables + p, &addedmap);
+           }
+         break;
+       case SOLVER_INSTALL_SOLVABLE_UPDATE:
+         /* dont allow downgrade */
+         addupdaterule(solv, pool->solvables + what, &addedmap, 0, 0, 1);
+         break;
+       }
+    }
+
+  /*
+   * if unstalls are disallowed, add update rules for every
+   * installed solvables in the hope to circumvent uninstall
+   * by upgrading
+   * 
+   */
+  
+#if 0
+  if (!solv->allowuninstall)
+    {
+      /* add update rule for every installed package */
+      for (i = solv->system->start; i < solv->system->start + solv->system->nsolvables; i++)
+        addupdaterule(solv, pool->solvables + i, &addedmap, solv->allowdowngrade, solv->allowarchchange, 1);
+    }
+#else  /* this is just to add the needed rpm rules to our set */
+  for (i = solv->system->start; i < solv->system->start + solv->system->nsolvables; i++)
+    addupdaterule(solv, pool->solvables + i, &addedmap, 1, 1, 1);
+#endif
+
+  /*
+   * first passed done
+   * 
+   * unify existing rules before going over all job rules
+   * 
+   */
+  
+  addrulesforsupplements(solv, &addedmap);
+
+  unifyrules(solv);    /* remove duplicate rpm rules */
+
+  /*
+   * at this point the system is always solvable,
+   * as an empty system (remove all packages) is a valid solution
+   */
+  if (pool->verbose) printf("decisions based on rpms: %d\n", solv->decisionq.count);
+
+  /*
+   * now add all job rules
+   */
+  
+  solv->jobrules = solv->nrules;
+
+  for (i = 0; i < job->count; i += 2)
+    {
+      how = job->elements[i];
+      what = job->elements[i + 1];
+      switch(how)
+       {
+       case SOLVER_INSTALL_SOLVABLE:                     /* install specific solvable */
+         if (solv->rc_output) {
+           Solvable *s = pool->solvables + what;
+           printf(">!> Installing %s from channel %s\n", id2str(pool, s->name), source_name(pool_source(pool, s)));
+         }
+          addrule(solv, what, 0);                         /* install by Id */
+         break;
+       case SOLVER_ERASE_SOLVABLE:
+          addrule(solv, -what, 0);                        /* remove by Id */
+         MAPSET(&noupdaterule, what);
+         break;
+       case SOLVER_INSTALL_SOLVABLE_NAME:                /* install by capability */
+       case SOLVER_INSTALL_SOLVABLE_PROVIDES:
+         QUEUEEMPTY(&q);
+         FOR_PROVIDES(p, pp, what)    /* check all providers */
+           {
+                                      /* if by name, ensure that the name matches */
+             if (how == SOLVER_INSTALL_SOLVABLE_NAME && pool->solvables[p].name != what)
+               continue;
+             queuepush(&q, p);
+           }
+         if (!q.count) {              /* no provider found -> abort */
+           fprintf(stderr, "Nothing provides '%s'\n", id2str(pool, what));
+           /* XXX make this a problem! */
+           return;
+           abort();
+         }
+
+         p = queueshift(&q);          /* get first provider */
+         if (!q.count)
+           d = 0;                     /* single provider ? -> make assertion */
+         else
+           d = pool_queuetowhatprovides(pool, &q);   /* get all providers */
+         addrule(solv, p, d);         /* add 'requires' rule */
+         break;
+       case SOLVER_ERASE_SOLVABLE_NAME:                  /* remove by capability */
+       case SOLVER_ERASE_SOLVABLE_PROVIDES:
+         FOR_PROVIDES(p, pp, what)
+           {
+                                      /* if by name, ensure that the name matches */
+             if (how == SOLVER_ERASE_SOLVABLE_NAME && pool->solvables[p].name != what)
+               continue;
+
+             addrule(solv, -p, 0);  /* add 'remove' rule */
+             MAPSET(&noupdaterule, p);
+           }
+         break;
+       case SOLVER_INSTALL_SOLVABLE_UPDATE:              /* find update for solvable */
+         addupdaterule(solv, pool->solvables + what, &addedmap, 0, 0, 0);
+         break;
+       }
+    }
+
+  if (pool->verbose) printf("problems so far: %d\n", solv->problems.count);
+  
+  /*
+   * now add policy rules
+   * 
+   */
+  
+  solv->systemrules = solv->nrules;
+
+  /*
+   * create rules for updating installed solvables
+   * 
+   * (Again ?)
+   * 
+   */
+  
+  if (!solv->allowuninstall)
+    {                                 /* loop over all installed solvables */
+      for (i = solv->system->start; i < solv->system->start + solv->system->nsolvables; i++)
+      {
+       if (!MAPTST(&noupdaterule, i)) /* if not marked as 'noupdate' */
+         addupdaterule(solv, pool->solvables + i, &addedmap, solv->allowdowngrade, solv->allowarchchange, 0);
+        else
+         addrule(solv, 0, 0);          /* place holder */
+      }
+      /* consistency check: we added a rule for _every_ system solvable */
+      if (solv->nrules - solv->systemrules != solv->system->nsolvables)
+       abort();
+    }
+
+  if (pool->verbose) printf("problems so far: %d\n", solv->problems.count);
+
+  /* create special weak system rules */
+  if (solv->system->nsolvables)
+    {
+      solv->weaksystemrules = xcalloc(solv->system->nsolvables, sizeof(Id));
+      for (i = 0; i < solv->system->nsolvables; i++)
+       {
+         findupdatepackages(solv, pool->solvables + solv->system->start + i, &q, (Map *)0, 1, 1);
+         if (q.count)
+           solv->weaksystemrules[i] = pool_queuetowhatprovides(pool, &q);
+       }
+    }
+
+  /* free unneeded memory */
+  mapfree(&addedmap);
+  mapfree(&noupdaterule);
+  queuefree(&q);
+
+  /*
+   * solve !
+   * 
+   */
+  
+  run_solver(solv, 1, 1);
+
+  /*
+   *
+   * print solver result
+   * 
+   */
+
+  if (pool->verbose) printf("-------------------------------------------------------------\n");
+
+  if (solv->problems.count)
+    {
+      Queue problems;
+      Queue solution;
+      Id *problem;
+      Id why;
+      int j;
+
+      if (!pool->verbose)
+       return;
+      clonequeue(&problems, &solv->problems);
+      queueinit(&solution);
+      printf("Encountered problems! Here are the solutions:\n");
+      problem = problems.elements;
+      for (i = 0; i < problems.count; i++)
+       {
+         Id v = problems.elements[i];
+         if (v == 0)
+           {
+             printf("====================================\n");
+             problem = problems.elements + i + 1;
+             continue;
+           }
+         refine_suggestion(solv, problem, v, &solution);
+         for (j = 0; j < solution.count; j++)
+           {
+             r = solv->rules + solution.elements[j];
+             why = solution.elements[j];
+#if 0
+             printrule(solv, r);
+#endif
+             if (why >= solv->jobrules && why < solv->systemrules)
+               {
+                 /* do a sloppy job of analyzing the job rule */
+                 if (r->p > 0)
+                   {
+                     s = pool->solvables + r->p;
+                     printf("- do not install %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+                   }
+                 else
+                   {
+                     s = pool->solvables - r->p;
+                     printf("- do not erase %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+                   }
+               }
+             else if (why >= solv->systemrules && why < solv->learntrules)
+               {
+                 s = pool->solvables + solv->system->start + (why - solv->systemrules);
+                 printf("- allow deinstallation/downgrade of %s-%s.%s [%d]\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch), why);
+               }
+             else
+               {
+                 abort();
+               }
+           }
+         printf("------------------------------------\n");
+       }
+      return;
+    }
+
+  printdecisions(solv);
+}
+
+
+// EOF
diff --git a/src/solver.h b/src/solver.h
new file mode 100644 (file)
index 0000000..776978c
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * solver.h
+ *
+ */
+
+#ifndef SOLVER_H
+#define SOLVER_H
+
+#include "pooltypes.h"
+#include "pool.h"
+#include "source.h"
+#include "queue.h"
+
+/* ----------------------------------------------
+ * Rule
+ *
+ *   providerN(B) == Package Id of package providing tag B
+ *   N = 1, 2, 3, in case of multiple providers
+ *
+ * A requires B : !A | provider1(B) | provider2(B)
+ *
+ * A conflicts B : (!A | !provider1(B)) & (!A | !provider2(B)) ...
+ *
+ * 'not' is encoded as a negative Id
+ * 
+ * Binary rule: p = first literal, d = 0, w2 = second literal, w1 = p
+ */
+
+typedef struct rule {
+  Id p;                        /* first literal in rule */
+  Id d;                        /* Id offset into 'list of providers terminated by 0' as used by whatprovides; pool->whatprovides + d */
+                       /* in case of binary rules, d == 0, w1 == p, w2 == other literal */
+  Id w1, w2;           /* watches, literals not-yet-decided */
+                                      /* if !w1, disabled */
+                                      /* if !w2, assertion, not rule */
+  Id n1, n2;           /* next rules in linked list, corresponding to w1,w2 */
+} Rule;
+
+typedef struct solver {
+  Pool *pool;
+  Source *system;
+
+  int fixsystem;                      /* repair errors in rpm dependency graph */
+  int allowdowngrade;                 /* allow to downgrade installed solvable */
+  int allowarchchange;                /* allow to change architecture of installed solvables */
+  int allowuninstall;                 /* allow removal of system solvables, else keep all installed solvables */
+  int updatesystem;                   /* distupgrade */
+  int allowvirtualconflicts;           /* false: conflicts on package name, true: conflicts on package provides */
+  
+  Rule *rules;                        /* all rules */
+  Id nrules;                          /* rpm rules */
+
+  Id jobrules;                        /* user rules */
+  Id systemrules;                     /* policy rules, e.g. keep packages installed or update. All literals > 0 */
+  Id learntrules;                     /* learnt rules */
+
+  Id *weaksystemrules;                 /* please try to install (r->d) */
+
+  Id *watches;                        /* Array of rule offsets
+                                       * watches has nsolvables*2 entries and is addressed from the middle
+                                       * middle-solvable : decision to conflict, offset point to linked-list of rules
+                                       * middle+solvable : decision to install: offset point to linked-list of rules
+                                       */
+
+  /* our decisions: */
+  Queue decisionq;
+  Queue decisionq_why;                /* index of rule, Offset into rules */
+  Id *decisionmap;                    /* map for all available solvables, > 0: level of decision when installed, < 0 level of decision when conflict */
+
+  /* learnt rule history */
+  Queue learnt_why;
+  Queue learnt_pool;
+
+  int propagate_index;
+
+  Queue problems;
+
+  int rc_output;                      /* output result compatible to redcarpet/zypp testsuite, set == 2 for pure rc (will suppress architecture) */
+} Solver;
+
+/*
+ * queue commands
+ */
+
+enum solvcmds {
+  SOLVCMD_NULL=0,
+  SOLVER_INSTALL_SOLVABLE,
+  SOLVER_ERASE_SOLVABLE,
+  SOLVER_INSTALL_SOLVABLE_NAME,
+  SOLVER_ERASE_SOLVABLE_NAME,
+  SOLVER_INSTALL_SOLVABLE_PROVIDES,
+  SOLVER_ERASE_SOLVABLE_PROVIDES,
+  SOLVER_INSTALL_SOLVABLE_UPDATE
+};
+
+extern Solver *solver_create(Pool *pool, Source *system);
+extern void solver_free(Solver *solv);
+extern void solve(Solver *solv, Queue *job);
+
+extern void prune_best_version_arch(Pool *pool, Queue *plist);
+
+void printdecisions(Solver *solv);
+
+
+#endif /* SOLVER_H */
diff --git a/src/source.c b/src/source.c
new file mode 100644 (file)
index 0000000..e3582c6
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * source.c
+ *
+ * Manage metadata coming from one repository
+ * 
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "source.h"
+#include "pool.h"
+#include "poolid_private.h"
+#include "util.h"
+
+#define IDARRAY_BLOCK     4095
+
+/*
+ * get name of source
+ */
+
+const char *
+source_name(const Source *source)
+{
+  return source->name;
+}
+
+
+/*
+ * create empty source
+ * and add to pool
+ */
+
+Source *
+pool_addsource_empty(Pool *pool)
+{
+  Source *source;
+
+  pool_freewhatprovides(pool);
+  source = (Source *)xcalloc(1, sizeof(*source));
+  pool->sources = (Source **)xrealloc(pool->sources, (pool->nsources + 1) * sizeof(Source *));
+  pool->sources[pool->nsources++] = source;
+  source->name = "empty";
+  source->pool = pool;
+  source->start = pool->nsolvables;
+  source->nsolvables = 0;
+  return source;
+}
+
+/*
+ * add Id to source
+ * olddeps = offset into idarraydata
+ * 
+ */
+
+unsigned int
+source_addid(Source *source, Offset olddeps, Id id)
+{
+  Id *idarray;
+  int idarraysize;
+  int i;
+  
+  idarray = source->idarraydata;
+  idarraysize = source->idarraysize;
+
+  if (!idarray)                               /* check idarray size */
+    {
+      idarray = (Id *)xmalloc((1 + IDARRAY_BLOCK) * sizeof(Id));
+      idarraysize = 1;
+      source->lastoff = 0;
+    }
+
+  if (!olddeps)                               /* no deps yet */
+    {   
+      olddeps = idarraysize;
+      if ((idarraysize & IDARRAY_BLOCK) == 0)
+        idarray = (Id *)xrealloc(idarray, (idarraysize + 1 + IDARRAY_BLOCK) * sizeof(Id));
+    }   
+  else if (olddeps == source->lastoff) /* append at end */
+    idarraysize--;
+  else                                /* check space */
+    {
+      i = olddeps;
+      olddeps = idarraysize;
+      for (; idarray[i]; i++)
+        {
+          if ((idarraysize & IDARRAY_BLOCK) == 0)
+            idarray = (Id *)xrealloc(idarray, (idarraysize + 1 + IDARRAY_BLOCK) * sizeof(Id));
+          idarray[idarraysize++] = idarray[i];
+        }
+      if ((idarraysize & IDARRAY_BLOCK) == 0)
+        idarray = (Id *)xrealloc(idarray, (idarraysize + 1 + IDARRAY_BLOCK) * sizeof(Id));
+    }
+  
+  idarray[idarraysize++] = id;        /* insert Id into array */
+
+  if ((idarraysize & IDARRAY_BLOCK) == 0)   /* realloc if at block boundary */
+    idarray = (Id *)xrealloc(idarray, (idarraysize + 1 + IDARRAY_BLOCK) * sizeof(Id));
+
+  idarray[idarraysize++] = ID_NULL;    /* ensure NULL termination */
+
+  source->idarraydata = idarray;
+  source->idarraysize = idarraysize;
+  source->lastoff = olddeps;
+
+  return olddeps;
+}
+
+
+/*
+ * add dependency (as Id) to source
+ * olddeps = offset into idarraydata
+ * isreq = 0 for normal dep
+ * isreq = 1 for requires
+ * isreq = 2 for pre-requires
+ * 
+ */
+
+unsigned int
+source_addid_dep(Source *source, Offset olddeps, Id id, int isreq)
+{
+  Id oid, *oidp, *marker = 0;
+
+  if (!olddeps)
+    return source_addid(source, olddeps, id);
+
+  if (!isreq)
+    {
+      for (oidp = source->idarraydata + olddeps; (oid = *oidp) != ID_NULL; oidp++)
+       {
+         if (oid == id)
+           return olddeps;
+       }
+      return source_addid(source, olddeps, id);
+    }
+
+  for (oidp = source->idarraydata + olddeps; (oid = *oidp) != ID_NULL; oidp++)
+    {
+      if (oid == SOLVABLE_PREREQMARKER)
+       marker = oidp;
+      else if (oid == id)
+       break;
+    }
+
+  if (oid)
+    {
+      if (marker || isreq == 1)
+        return olddeps;
+      marker = oidp++;
+      for (; (oid = *oidp) != ID_NULL; oidp++)
+        if (oid == SOLVABLE_PREREQMARKER)
+          break;
+      if (!oid)
+        {
+          oidp--;
+          if (marker < oidp)
+            memmove(marker, marker + 1, (oidp - marker) * sizeof(Id));
+          *oidp = SOLVABLE_PREREQMARKER;
+          return source_addid(source, olddeps, id);
+        }
+      while (oidp[1])
+        oidp++;
+      memmove(marker, marker + 1, (oidp - marker) * sizeof(Id));
+      *oidp = id;
+      return olddeps;
+    }
+  if (isreq == 2 && !marker)
+    olddeps = source_addid(source, olddeps, SOLVABLE_PREREQMARKER);
+  else if (isreq == 1 && marker)
+    {
+      *marker++ = id;
+      id = *--oidp;
+      if (marker < oidp)
+        memmove(marker + 1, marker, (oidp - marker) * sizeof(Id));
+      *marker = SOLVABLE_PREREQMARKER;
+    }
+  return source_addid(source, olddeps, id);
+}
+
+
+/*
+ * reserve Ids
+ * make space for 'num' more dependencies
+ */
+
+unsigned int
+source_reserve_ids(Source *source, unsigned int olddeps, int num)
+{
+  num++;       /* room for trailing ID_NULL */
+
+  if (!source->idarraysize)           /* ensure buffer space */
+    {
+      source->idarraysize = 1;
+      source->idarraydata = (Id *)xmalloc(((1 + num + IDARRAY_BLOCK) & ~IDARRAY_BLOCK) * sizeof(Id));
+      source->lastoff = 1;
+      return 1;
+    }
+
+  if (olddeps && olddeps != source->lastoff)   /* if not appending */
+    {
+      /* can't insert into idarray, this would invalidate all 'larger' offsets
+       * so create new space at end and move existing deps there.
+       * Leaving 'hole' at old position.
+       */
+      
+      Id *idstart, *idend;
+      int count;
+
+      for (idstart = idend = source->idarraydata + olddeps; *idend++; )   /* find end */
+       ;
+      count = idend - idstart - 1 + num;              /* new size */
+
+      /* realloc if crossing block boundary */
+      if (((source->idarraysize - 1) | IDARRAY_BLOCK) != ((source->idarraysize + count - 1) | IDARRAY_BLOCK))
+       source->idarraydata = (Id *)xrealloc(source->idarraydata, ((source->idarraysize + count + IDARRAY_BLOCK) & ~IDARRAY_BLOCK) * sizeof(Id));
+
+      /* move old deps to end */
+      olddeps = source->lastoff = source->idarraysize;
+      memcpy(source->idarraydata + olddeps, idstart, count - num);
+      source->idarraysize = olddeps + count - num;
+
+      return olddeps;
+    }
+
+  if (olddeps)                        /* appending */
+    source->idarraysize--;
+
+  /* realloc if crossing block boundary */
+  if (((source->idarraysize - 1) | IDARRAY_BLOCK) != ((source->idarraysize + num - 1) | IDARRAY_BLOCK))
+    source->idarraydata = (Id *)xrealloc(source->idarraydata, ((source->idarraysize + num + IDARRAY_BLOCK) & ~IDARRAY_BLOCK) * sizeof(Id));
+
+  /* appending or new */
+  source->lastoff = olddeps ? olddeps : source->idarraysize;
+
+  return source->lastoff;
+}
+
+
+/*
+ * remove source from pool
+ * 
+ */
+
+void
+pool_freesource(Pool *pool, Source *source)
+{
+  int i, nsolvables;
+
+  pool_freewhatprovides(pool);
+
+  for (i = 0; i < pool->nsources; i++) /* find source in pool */
+    {
+      if (pool->sources[i] == source)
+       break;
+    }
+  if (i == pool->nsources)            /* source not in pool, return */
+    return;
+
+  /* close gap
+   * all sources point into pool->solvables _relatively_ to source->start
+   * so closing the gap only needs adaption of source->start for all
+   * other sources.
+   */
+  
+  nsolvables = source->nsolvables;
+  if (pool->nsolvables > source->start + nsolvables)
+    memmove(pool->solvables + source->start, pool->solvables + source->start + nsolvables, (pool->nsolvables - source->start - nsolvables) * sizeof(Solvable));
+  pool->nsolvables -= nsolvables;
+
+  for (; i < pool->nsources - 1; i++)
+    {
+      pool->sources[i] = pool->sources[i + 1];   /* remove source */
+      pool->sources[i]->start -= nsolvables;     /* adapt start offset of remaining sources */
+    }
+  pool->nsources = i;
+
+  xfree(source->idarraydata);
+  xfree(source->rpmdbid);
+  xfree(source);
+}
+
+// EOF
diff --git a/src/source.h b/src/source.h
new file mode 100644 (file)
index 0000000..a5b1a99
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * source.h
+ * 
+ */
+
+#ifndef SOURCE_H
+#define SOURCE_H
+
+#include "pooltypes.h"
+
+typedef struct _Source {
+  const char *name;
+  struct _Pool *pool;                 /* pool containing source data */
+  int start;                          /* start of this source solvables within pool->solvables */
+  int nsolvables;                     /* number of solvables source is contributing to pool */
+
+  Id *idarraydata;                    /* array of metadata Ids, solvable dependencies are offsets into this array */
+  int idarraysize;
+  Offset lastoff;
+
+  Id *rpmdbid;
+} Source;
+
+extern unsigned int source_addid(Source *source, unsigned int olddeps, Id id);
+extern unsigned int source_addid_dep(Source *source, unsigned int olddeps, Id id, int isreq);
+extern unsigned int source_reserve_ids(Source *source, unsigned int olddeps, int num);
+extern Source *pool_addsource_empty(Pool *pool);
+
+extern const char *source_name(const Source *source);
+#endif /* SOURCE_H */
diff --git a/src/source_solv.c b/src/source_solv.c
new file mode 100644 (file)
index 0000000..06d0d81
--- /dev/null
@@ -0,0 +1,588 @@
+/*
+ * source_solv.c
+ * 
+ * Read the binary dump of a Source and create a Source * from it
+ * 
+ *  See
+ *   Source *pool_addsource_solv(Pool *pool, FILE *fp)
+ * below
+ * 
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "source_solv.h"
+#include "util.h"
+
+#define INTERESTED_START       2
+#define INTERESTED_END         13
+
+/*-----------------------------------------------------------------*/
+/* .solv read functions */
+
+/*
+ * read u32
+ */
+
+static unsigned int
+read_u32(FILE *fp)
+{
+  int c, i;
+  unsigned int x = 0;
+
+  for (i = 0; i < 4; i++)
+    {
+      c = getc(fp);
+      if (c == EOF)
+       {
+         fprintf(stderr, "unexpected EOF\n");
+         exit(1);
+       }
+      x = (x << 8) | c;
+    }
+  return x;
+}
+
+
+/*
+ * read u8
+ */
+
+static unsigned int
+read_u8(FILE *fp)
+{
+  int c;
+  c = getc(fp);
+  if (c == EOF)
+    {
+      fprintf(stderr, "unexpected EOF\n");
+      exit(1);
+    }
+  return c;
+}
+
+
+/*
+ * read Id
+ */
+
+static Id
+read_id(FILE *fp, Id max)
+{
+  unsigned int x = 0;
+  int c, i;
+
+  for (i = 0; i < 5; i++)
+    {
+      c = getc(fp);
+      if (c == EOF)
+       {
+         fprintf(stderr, "unexpected EOF\n");
+         exit(1);
+       }
+      if (!(c & 128))
+       {
+         x = (x << 7) | c;
+         if (x >= max)
+           {
+             fprintf(stderr, "read_id: id too large (%u/%u)\n", x, max);
+             exit(1);
+           }
+         return x;
+       }
+      x = (x << 7) ^ c ^ 128;
+    }
+  fprintf(stderr, "read_id: id too long\n");
+  exit(1);
+}
+
+
+/*
+ * read array of Ids
+ */
+
+static Id *
+read_idarray(FILE *fp, Id max, Id *map, Id *store)
+{
+  unsigned int x = 0;
+  int c;
+  for (;;)
+    {
+      c = getc(fp);
+      if (c == EOF)
+       {
+         fprintf(stderr, "unexpected EOF\n");
+         exit(1);
+       }
+      if ((c & 128) == 0)
+       {
+         x = (x << 6) | (c & 63);
+         *store++ = map[x];
+         if ((c & 64) == 0)
+           {
+             *store++ = 0;
+             return store;
+           }
+         x = 0;
+         continue;
+       }
+      x = (x << 7) ^ c ^ 128;
+    }
+}
+
+
+/*-----------------------------------------------------------------*/
+
+typedef struct solvdata {
+  int type;
+  Id id;
+  unsigned int size;
+} SolvData;
+
+
+// ----------------------------------------------
+
+/*
+ * read source from .solv file
+ *  and add it to pool
+ */
+
+Source *
+pool_addsource_solv(Pool *pool, FILE *fp, const char *sourcename)
+{
+  int i, j, l;
+  unsigned int numid, numrel, numsolv, numsrcdata, numsolvdata;
+  int numsolvdatabits, type;
+  Offset sizeid;
+  Offset *str;                        /* map Id -> Offset into string space */
+  char *strsp;                        /* source string space */
+  char *sp;                           /* pointer into string space */
+  Id *idmap;                          /* map of source Ids to pool Ids */
+  Id id;
+  unsigned int hashmask, h;
+  int hh;
+  Id *hashtbl;
+  Id name, evr, did;
+  int flags;
+  Reldep *ran;
+  SolvData *solvdata;
+  unsigned int size, size_str, size_idarray;
+  Source *source;
+  Id *idarraydatap, *ida;
+  unsigned int databits;
+  Solvable *s;
+
+  if (read_u32(fp) != ('S' << 24 | 'O' << 16 | 'L' << 8 | 'V'))
+    {
+      fprintf(stderr, "not a SOLV file\n");
+      exit(1);
+    }
+  if (read_u32(fp) != SOLV_VERSION)
+    {
+      fprintf(stderr, "unsupported SOLV version\n");
+      exit(1);
+    }
+
+                                      /* create empty Source */
+  source = pool_addsource_empty(pool);
+  pool_freeidhashes(pool);
+
+  source->name = sourcename;
+  
+  numid = read_u32(fp);
+  numrel = read_u32(fp);
+  numsolv= read_u32(fp);
+
+  sizeid = read_u32(fp);              /* size of string+Id space */
+
+  /*
+   * read strings and Ids
+   * 
+   */
+
+  
+  /*
+   * alloc buffers
+   */
+                                      /* alloc string buffer */
+  strsp = (char *)xrealloc(pool->stringspace, pool->sstrings + sizeid + 1);
+                                      /* alloc string offsets (Id -> Offset into string space) */
+  str = (Offset *)xrealloc(pool->strings, (pool->nstrings + numid) * sizeof(Offset));
+
+  pool->stringspace = strsp;
+  pool->strings = str;                /* array of offsets into strsp, indexed by Id */
+
+  /* point to _BEHIND_ already allocated string/Id space */
+  strsp += pool->sstrings;
+
+                                      /* alloc id map for name and rel Ids */
+  idmap = (Id *)xcalloc(numid + numrel, sizeof(Id));
+
+  /*
+   * read new source at end of pool
+   */
+  
+  if (fread(strsp, sizeid, 1, fp) != 1)
+    {
+      fprintf(stderr, "read error while reading strings\n");
+      exit(1);
+    }
+  strsp[sizeid] = 0;                  /* make string space \0 terminated */
+  sp = strsp;
+
+  /*
+   * build hashes for all read strings
+   * 
+   */
+  
+  hashmask = mkmask(pool->nstrings + numid);
+
+#if 0
+  printf("read %d strings\n", numid);
+  printf("string hash buckets: %d\n", hashmask + 1);
+#endif
+
+  /*
+   * ensure sufficient hash size
+   */
+  
+  hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
+
+  /*
+   * fill hashtable with strings already in pool
+   */
+  
+  for (i = 1; i < pool->nstrings; i++)  /* leave out our dummy zero id */
+    {
+      h = strhash(pool->stringspace + pool->strings[i]) & hashmask;
+      hh = HASHCHAIN_START;
+      while (hashtbl[h])
+        h = HASHCHAIN_NEXT(h, hh, hashmask);
+      hashtbl[h] = i;
+    }
+
+  /*
+   * run over string space, calculate offsets
+   * 
+   * build id map (maps solv Id -> pool Id)
+   */
+  
+  for (i = 1; i < numid; i++)
+    {
+      if (sp >= strsp + sizeid)
+       {
+         fprintf(stderr, "not enough strings\n");
+         exit(1);
+       }
+      if (!*sp)                               /* empty string */
+       {
+         idmap[i] = ID_EMPTY;
+         sp++;
+         continue;
+       }
+
+      /* find hash slot */
+      h = strhash(sp) & hashmask;
+      hh = HASHCHAIN_START;
+      for (;;)
+       {
+         id = hashtbl[h];
+         if (id == 0)
+           break;
+         if (!strcmp(pool->stringspace + pool->strings[id], sp))
+           break;                     /* existing string */
+         h = HASHCHAIN_NEXT(h, hh, hashmask);
+       }
+
+      /* length == offset to next string */
+      l = strlen(sp) + 1;
+      if (id == ID_NULL)              /* end of hash chain -> new string */
+       {
+         id = pool->nstrings++;
+         hashtbl[h] = id;
+         str[id] = pool->sstrings;    /* save Offset */
+         if (sp != pool->stringspace + pool->sstrings)   /* not at end-of-buffer */
+           memmove(pool->stringspace + pool->sstrings, sp, l);   /* append to pool buffer */
+          pool->sstrings += l;
+       }
+      idmap[i] = id;                  /* source relative -> pool relative */
+      sp += l;                        /* next string */
+    }
+  xfree(hashtbl);
+  pool_shrink_strings(pool);          /* vacuum */
+
+  
+  /*
+   * read RelDeps
+   * 
+   */
+  
+  if (numrel)
+    {
+      /* extend rels */
+      ran = (Reldep *)xrealloc(pool->rels, (pool->nrels + numrel) * sizeof(Reldep));
+      if (!ran)
+       {
+         fprintf(stderr, "no mem for rel space\n");
+         exit(1);
+       }
+      pool->rels = ran;               /* extended rel space */
+
+      hashmask = mkmask(pool->nrels + numrel);
+#if 0
+      printf("read %d rels\n", numrel);
+      printf("rel hash buckets: %d\n", hashmask + 1);
+#endif
+      /*
+       * prep hash table with already existing RelDeps
+       */
+      
+      hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
+      for (i = 1; i < pool->nrels; i++)
+       {
+         h = relhash(ran[i].name, ran[i].evr, ran[i].flags) & hashmask;
+         hh = HASHCHAIN_START;
+         while (hashtbl[h])
+           h = HASHCHAIN_NEXT(h, hh, hashmask);
+         hashtbl[h] = i;
+       }
+
+      /*
+       * read RelDeps from source
+       */
+      
+      for (i = 0; i < numrel; i++)
+       {
+         name = read_id(fp, numid);    /* read (source relative) Ids */
+         evr = read_id(fp, numid);
+         flags = read_u8(fp);
+         name = idmap[name];           /* map to (pool relative) Ids */
+         evr = idmap[evr];
+         h = relhash(name, evr, flags) & hashmask;
+         hh = HASHCHAIN_START;
+         for (;;)
+           {
+             id = hashtbl[h];
+             if (id == ID_NULL)        /* end of hash chain */
+               break;
+             if (ran[id].name == name && ran[id].evr == evr && ran[id].flags == flags)
+               break;
+             h = HASHCHAIN_NEXT(h, hh, hashmask);
+           }
+         if (id == ID_NULL)            /* new RelDep */
+           {
+             id = pool->nrels++;
+             hashtbl[h] = id;
+             ran[id].name = name;
+             ran[id].evr = evr;
+             ran[id].flags = flags;
+           }
+         idmap[i + numid] = MAKERELDEP(id);   /* fill Id map */
+       }
+      xfree(hashtbl);
+      pool_shrink_rels(pool);          /* vacuum */
+    }
+
+  /*
+   * read (but dont store) source data
+   */
+
+#if 0
+  printf("read source data\n");
+#endif
+  numsrcdata = read_u32(fp);
+  for (i = 0; i < numsrcdata; i++)
+    {
+      type = read_u8(fp);
+      id = idmap[read_id(fp, numid)];
+      switch(type)
+       {
+       case TYPE_ID:
+          read_id(fp, numid + numrel);   /* just check Id */
+         break;
+       case TYPE_U32:
+          read_u32(fp);
+         break;
+       case TYPE_STR:
+         while(read_u8(fp) != 0)
+           ;
+         break;
+       default:
+          fprintf(stderr, "unknown type\n");
+         exit(0);
+       }
+    }
+
+
+  /*
+   * read solvables
+   */
+  
+#if 0
+  printf("read solvable data info\n");
+#endif
+  numsolvdata = read_u32(fp);
+  numsolvdatabits = 0;
+  solvdata = (SolvData *)xmalloc(numsolvdata * sizeof(SolvData));
+  size_idarray = 0;
+  size_str = 0;
+
+  for (i = 0; i < numsolvdata; i++)
+    {
+      type = read_u8(fp);
+      solvdata[i].type = type;
+      if ((type & TYPE_BITMAP) != 0)
+       {
+         type ^= TYPE_BITMAP;
+         numsolvdatabits++;
+       }
+      id = idmap[read_id(fp, numid)];
+#if 0
+      printf("#%d: %s\n", i, id2str(pool, id));
+#endif
+      solvdata[i].id = id;
+      size = read_u32(fp);
+      solvdata[i].size = size;
+      if (id >= INTERESTED_START && id <= INTERESTED_END)
+       {
+         if (type == TYPE_STR)
+           size_str += size;
+         if (type == TYPE_IDARRAY)
+           size_idarray += size;
+       }
+    }
+
+  if (numsolvdatabits >= 32)
+    {
+      fprintf(stderr, "too many data map bits\n");
+      exit(1);
+    }
+  if (size_idarray)
+    source->idarraydata = (Id *)xmalloc(sizeof(Id) * size_idarray);
+  idarraydatap = source->idarraydata;
+
+  /* alloc solvables */
+  pool->solvables = (Solvable *)xrealloc(pool->solvables, (pool->nsolvables + numsolv) * sizeof(Solvable));
+
+  if (numsolv)                        /* clear newly allocated area */
+    memset(pool->solvables + pool->nsolvables, 0, numsolv * sizeof(Solvable));
+  source->start = pool->nsolvables;
+  source->nsolvables = numsolv;
+
+  /*
+   * read solvables
+   */
+  
+#if 0
+  printf("read solvables\n");
+#endif
+  for (i = 0, s = pool->solvables + source->start; i < numsolv; i++, s++)
+    {
+      databits = 0;
+      if (numsolvdatabits)
+       {
+         for (j = 0; j < (numsolvdatabits + 7) >> 3; j++)
+           databits = (databits << 8) | read_u8(fp);
+       }
+      for (j = 0; j < numsolvdata; j++)
+       {
+         type = solvdata[j].type;
+         if ((type & TYPE_BITMAP) != 0)
+           {
+             if (!(databits & 1))
+               {
+                 databits >>= 1;
+                 continue;
+               }
+             databits >>= 1;
+             type ^= TYPE_BITMAP;
+           }
+         id = solvdata[j].id;
+         switch (type)
+           {
+           case TYPE_ID:
+             did = idmap[read_id(fp, numid + numrel)];
+             if (id == SOLVABLE_NAME)
+               s->name = did;
+             else if (id == SOLVABLE_ARCH)
+               s->arch = did;
+             else if (id == SOLVABLE_EVR)
+               s->evr= did;
+#if 0
+             printf("%s -> %s\n", id2str(pool, id), id2str(pool, did));
+#endif
+             break;
+           case TYPE_U32:
+             h = read_u32(fp);
+#if 0
+             printf("%s -> %u\n", id2str(pool, id), h);
+#endif
+             if (id == RPM_RPMDBID)
+               {
+                 if (!source->rpmdbid)
+                   source->rpmdbid = (Id *)xcalloc(numsolv, sizeof(Id));
+                 source->rpmdbid[i] = h;
+               }
+             break;
+           case TYPE_STR:
+             while(read_u8(fp) != 0)
+               ;
+             break;
+           case TYPE_IDARRAY:
+             if (id < INTERESTED_START || id > INTERESTED_END)
+               {
+                 /* not interested in array */
+                 while ((read_u8(fp) & 0xc0) != 0)
+                   ;
+                 break;
+               }
+             ida = idarraydatap;
+             idarraydatap = read_idarray(fp, numid + numrel, idmap, ida);
+             if (id == SOLVABLE_PROVIDES)
+               s->provides = ida;
+             else if (id == SOLVABLE_OBSOLETES)
+               s->obsoletes = ida;
+             else if (id == SOLVABLE_CONFLICTS)
+               s->conflicts = ida;
+             else if (id == SOLVABLE_REQUIRES)
+               s->requires = ida;
+             else if (id == SOLVABLE_RECOMMENDS)
+               s->recommends= ida;
+             else if (id == SOLVABLE_SUPPLEMENTS)
+               s->supplements = ida;
+             else if (id == SOLVABLE_SUGGESTS)
+               s->suggests = ida;
+             else if (id == SOLVABLE_ENHANCES)
+               s->enhances = ida;
+             else if (id == SOLVABLE_FRESHENS)
+               s->freshens = ida;
+#if 0
+             printf("%s ->\n", id2str(pool, id));
+             for (; *ida; ida++)
+               printf("  %s%s%s\n", id2str(pool, *ida), id2rel(pool, *ida), id2evr(pool, *ida));
+#endif
+             break;
+           }
+       }
+    }
+
+  if (idarraydatap > source->idarraydata + size_idarray)
+    {
+      fprintf(stderr, "idarray overflow\n");
+      exit(1);
+    }
+
+  xfree(idmap);
+  xfree(solvdata);
+
+  pool->nsolvables += numsolv;
+
+  return source;
+}
+
+// EOF
diff --git a/src/source_solv.h b/src/source_solv.h
new file mode 100644 (file)
index 0000000..33f40c1
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * source_solv.h
+ * 
+ */
+
+#ifndef SOURCE_SOLVE_H
+#define SOURCE_SOLVE_H
+
+#include "pool.h"
+#include "source.h"
+
+extern Source *pool_addsource_solv(Pool *pool, FILE *fp, const char *name);
+
+#endif /* SOURCE_SOLVE_H */
diff --git a/src/util.c b/src/util.c
new file mode 100644 (file)
index 0000000..4b95036
--- /dev/null
@@ -0,0 +1,74 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "util.h"
+
+void *
+xmalloc(size_t len)
+{
+  void *r = malloc(len ? len : 1);
+  if (r)
+    return r;
+  fprintf(stderr, "Out of memory allocating %zu bytes!\n", len);
+  exit(1);
+}
+
+void *
+xmalloc2(size_t num, size_t len)
+{
+  if (len && (num * len) / len != num)
+    {
+      fprintf(stderr, "Out of memory allocating %zu*%zu bytes!\n", num, len);
+      exit(1);
+    }
+  return xmalloc(num * len);
+}
+
+void *
+xrealloc(void *old, size_t len)
+{
+  if (old == 0)
+    old = malloc(len ? len : 1);
+  else
+    old = realloc(old, len ? len : 1);
+  if (old)
+    return old;
+  fprintf(stderr, "Out of memory reallocating %zu bytes!\n", len);
+  exit(1);
+}
+
+void *
+xrealloc2(void *old, size_t num, size_t len)
+{
+  if (len && (num * len) / len != num)
+    {
+      fprintf(stderr, "Out of memory allocating %zu*%zu bytes!\n", num, len);
+      exit(1);
+    }
+  return xrealloc(old, num * len);
+}
+
+void *
+xcalloc(size_t num, size_t len)
+{
+  void *r;
+  if (num == 0 || len == 0)
+    r = malloc(1);
+  else
+    r = calloc(num, len);
+  if (r)
+    return r;
+  fprintf(stderr, "Out of memory allocating %zu bytes!\n", num * len);
+  exit(1);
+}
+
+void *
+xfree(void *mem)
+{
+  if (mem)
+    free(mem);
+  return 0;
+}
+
diff --git a/src/util.h b/src/util.h
new file mode 100644 (file)
index 0000000..1b90cb3
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * util.h
+ * 
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+extern void *xmalloc(size_t);
+extern void *xmalloc2(size_t, size_t);
+extern void *xcalloc(size_t, size_t);
+extern void *xrealloc(void *, size_t);
+extern void *xrealloc2(void *, size_t, size_t);
+extern void *xfree(void *);
+
+#endif /* UTIL_H */
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644 (file)
index 0000000..d237497
--- /dev/null
@@ -0,0 +1,51 @@
+noinst_PROGRAMS = rpmdb2solv rpmmd2solv helix2solv susetags2solv patchxml2solv dumpsolv
+
+INCLUDES =  \
+       -I$(top_srcdir)/src
+
+LIBS = \
+       $(top_builddir)/src/libsatsolver.la
+
+noinst_HEADERS = source_write.h
+
+rpmdb2solv_SOURCES = \
+       rpmdb2solv.c    \
+       source_rpmdb.h  \
+       source_rpmdb.c  \
+       source_write.c
+
+rpmdb2solv_LDADD = -ldb-4.3
+
+rpmmd2solv_SOURCES = \
+       rpmmd2solv.c    \
+       source_rpmmd.h  \
+       source_rpmmd.c  \
+       source_write.c
+
+rpmmd2solv_LDADD = -lexpat
+
+helix2solv_SOURCES = \
+       helix2solv.c    \
+       source_helix.h  \
+       source_helix.c  \
+       source_write.c
+
+helix2solv_LDADD = -lexpat
+
+susetags2solv_SOURCES = \
+       susetags2solv.c         \
+       source_susetags.h       \
+       source_susetags.c       \
+       source_write.c
+
+patchxml2solv_SOURCES = \
+       patchxml2solv.c         \
+       source_patchxml.h       \
+       source_patchxml.c       \
+       source_write.c
+
+patchxml2solv_LDADD = -lexpat
+
+dumpsolv_SOURCES = \
+       dumpsolv.c
+
diff --git a/tools/dumpsolv.c b/tools/dumpsolv.c
new file mode 100644 (file)
index 0000000..e4d66c3
--- /dev/null
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "pool.h"
+#include "source_solv.h"
+
+static void
+printids(Pool *pool, char *kind, Id *ids)
+{
+  Id id;
+  if (!ids)
+    return;
+  printf("%s:\n", kind);
+  while((id = *ids++) != 0)
+    printf("  %s%s%s\n", id2str(pool, id), id2rel(pool, id), id2evr(pool, id));
+}
+
+int main(int argc, char **argv)
+{
+  Source *source;
+  Pool *pool;
+  int i;
+  Solvable *s;
+
+  if (argc != 1)
+    {
+      if (freopen(argv[1], "r", stdin) == 0)
+       {
+         perror(argv[1]);
+         exit(1);
+       }
+    }
+  pool = pool_create();
+  source = pool_addsource_solv(pool, stdin, "");
+  printf("source contains %d solvables\n", source->nsolvables);
+  for (i = source->start; i < source->start + source->nsolvables; i++)
+    {
+      s = pool->solvables + i;
+      printf("\n");
+      printf("solvable %d:\n", i);
+      printf("name: %s %s %s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch));
+      printids(pool, "provides", s->provides);
+      printids(pool, "obsoletes", s->obsoletes);
+      printids(pool, "conflicts", s->conflicts);
+      printids(pool, "requires", s->requires);
+      printids(pool, "recommends", s->recommends);
+      printids(pool, "suggests", s->suggests);
+      printids(pool, "supplements", s->supplements);
+      printids(pool, "enhances", s->enhances);
+      printids(pool, "freshens", s->freshens);
+    }
+  exit(0);
+}
diff --git a/tools/helix2solv.c b/tools/helix2solv.c
new file mode 100644 (file)
index 0000000..5abd8b4
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * helix2solv.c
+ * 
+ * parse 'helix' type xml and write out .solv file
+ *
+ * reads from stdin
+ * writes to stdout
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "source_helix.h"
+#include "source_write.h"
+
+int
+main(int argc, char **argv)
+{
+  Pool *pool = pool_create();
+  Source *source = pool_addsource_helix(pool, stdin);
+  pool_writesource(pool, source, stdout);
+  pool_free(pool);
+  exit(0);
+}
diff --git a/tools/patchxml2solv.c b/tools/patchxml2solv.c
new file mode 100644 (file)
index 0000000..5fb549c
--- /dev/null
@@ -0,0 +1,20 @@
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "source_patchxml.h"
+#include "source_write.h"
+
+int
+main(int argc, char **argv)
+{
+  Pool *pool = pool_create();
+  Source *source = pool_addsource_patchxml(pool, stdin);
+  pool_writesource(pool, source, stdout);
+  pool_free(pool);
+  exit(0);
+}
diff --git a/tools/rpmdb2solv.c b/tools/rpmdb2solv.c
new file mode 100644 (file)
index 0000000..e4239fb
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * rpmdb2solv
+ * 
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "source_rpmdb.h"
+#include "source_solv.h"
+#include "source_write.h"
+
+int
+main(int argc, char **argv)
+{
+  Pool *pool = pool_create();
+  Source *ref = NULL;
+  FILE *fp;
+
+  if (argc != 1)
+    {
+      Pool *refpool = pool;
+      if ((fp = fopen(argv[1], "r")) == NULL)
+       {
+         perror(argv[1]);
+         exit(0);
+       }
+      ref = pool_addsource_solv(refpool, fp, "rpmdb");
+      fclose(fp);
+    }
+
+  Source *source = pool_addsource_rpmdb(pool, ref);
+  if (ref)
+    {
+      if (ref->pool != pool)
+       pool_free(ref->pool);
+      else
+       pool_freesource(pool, ref);
+      ref = NULL;
+    }
+
+  pool_writesource(pool, source, stdout);
+  pool_free(pool);
+
+  exit(0);
+}
diff --git a/tools/rpmmd2solv.c b/tools/rpmmd2solv.c
new file mode 100644 (file)
index 0000000..9ca1ba2
--- /dev/null
@@ -0,0 +1,20 @@
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "source_rpmmd.h"
+#include "source_write.h"
+
+int
+main(int argc, char **argv)
+{
+  Pool *pool = pool_create();
+  Source *source = pool_addsource_rpmmd(pool, stdin);
+  pool_writesource(pool, source, stdout);
+  pool_free(pool);
+  exit(0);
+}
diff --git a/tools/source_helix.c b/tools/source_helix.c
new file mode 100644 (file)
index 0000000..8c367aa
--- /dev/null
@@ -0,0 +1,772 @@
+/*
+ * source_helix.c
+ * 
+ * Parse 'helix' XML representation
+ * and create 'source'
+ * 
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <expat.h>
+
+#include "source_helix.h"
+#include "evr.h"
+
+
+/* XML parser states */
+
+enum state {
+  STATE_START,
+  STATE_CHANNEL,
+  STATE_SUBCHANNEL,
+  STATE_PACKAGE,
+  STATE_NAME,
+  STATE_HISTORY,
+  STATE_UPDATE,
+  STATE_EPOCH,
+  STATE_VERSION,
+  STATE_RELEASE,
+  STATE_ARCH,
+  STATE_PROVIDES,
+  STATE_PROVIDESENTRY,
+  STATE_REQUIRES,
+  STATE_REQUIRESENTRY,
+  STATE_OBSOLETES,
+  STATE_OBSOLETESENTRY,
+  STATE_CONFLICTS,
+  STATE_CONFLICTSENTRY,
+  STATE_RECOMMENDS,
+  STATE_RECOMMENDSENTRY,
+  STATE_SUPPLEMENTS,
+  STATE_SUPPLEMENTSENTRY,
+  STATE_SUGGESTS,
+  STATE_SUGGESTSENTRY,
+  STATE_ENHANCES,
+  STATE_ENHANCESENTRY,
+  STATE_FRESHENS,
+  STATE_FRESHENSENTRY,
+
+  STATE_SELECTTION,
+  STATE_PATTERN,
+  STATE_ATOM,
+  STATE_PATCH,
+
+  STATE_PEPOCH,
+  STATE_PVERSION,
+  STATE_PRELEASE,
+  STATE_PARCH,
+
+  NUMSTATES
+};
+
+#define PACK_BLOCK 255
+
+struct stateswitch {
+  enum state from;
+  char *ename;
+  enum state to;
+  int docontent;
+};
+
+static struct stateswitch stateswitches[] = {
+  { STATE_START,       "channel",         STATE_CHANNEL, 0 },
+  { STATE_CHANNEL,     "subchannel",      STATE_SUBCHANNEL, 0 },
+  { STATE_SUBCHANNEL,  "package",         STATE_PACKAGE, 0 },
+  { STATE_SUBCHANNEL,  "selection",       STATE_PACKAGE, 0 },
+  { STATE_SUBCHANNEL,  "pattern",         STATE_PACKAGE, 0 },
+  { STATE_SUBCHANNEL,  "atom",            STATE_PACKAGE, 0 },
+  { STATE_SUBCHANNEL,  "patch",           STATE_PACKAGE, 0 },
+  { STATE_PACKAGE,     "name",            STATE_NAME, 1 },
+  { STATE_PACKAGE,     "epoch",           STATE_PEPOCH, 1 },
+  { STATE_PACKAGE,     "version",         STATE_PVERSION, 1 },
+  { STATE_PACKAGE,     "release",         STATE_PRELEASE, 1 },
+  { STATE_PACKAGE,     "arch",            STATE_PARCH, 1 },
+  { STATE_PACKAGE,     "history",         STATE_HISTORY, 0 },
+  { STATE_PACKAGE,     "provides",        STATE_PROVIDES, 0 },
+  { STATE_PACKAGE,     "requires",        STATE_REQUIRES, 0 },
+  { STATE_PACKAGE,     "obsoletes",       STATE_OBSOLETES , 0 },
+  { STATE_PACKAGE,     "conflicts",       STATE_CONFLICTS , 0 },
+  { STATE_PACKAGE,     "recommends" ,     STATE_RECOMMENDS , 0 },
+  { STATE_PACKAGE,     "supplements",     STATE_SUPPLEMENTS, 0 },
+  { STATE_PACKAGE,     "suggests",        STATE_SUGGESTS, 0 },
+  { STATE_PACKAGE,     "enhances",        STATE_ENHANCES, 0 },
+  { STATE_PACKAGE,     "freshens",        STATE_FRESHENS, 0 },
+
+  { STATE_HISTORY,     "update",          STATE_UPDATE, 0 },
+  { STATE_UPDATE,      "epoch",           STATE_EPOCH, 1 },
+  { STATE_UPDATE,      "version",         STATE_VERSION, 1 },
+  { STATE_UPDATE,      "release",         STATE_RELEASE, 1 },
+  { STATE_UPDATE,      "arch",            STATE_ARCH, 1 },
+
+  { STATE_PROVIDES,    "dep",             STATE_PROVIDESENTRY, 0 },
+  { STATE_REQUIRES,    "dep",             STATE_REQUIRESENTRY, 0 },
+  { STATE_OBSOLETES,   "dep",             STATE_OBSOLETESENTRY, 0 },
+  { STATE_CONFLICTS,   "dep",             STATE_CONFLICTSENTRY, 0 },
+  { STATE_RECOMMENDS,  "dep",             STATE_RECOMMENDSENTRY, 0 },
+  { STATE_SUPPLEMENTS, "dep",             STATE_SUPPLEMENTSENTRY, 0 },
+  { STATE_SUGGESTS,    "dep",             STATE_SUGGESTSENTRY, 0 },
+  { STATE_ENHANCES,    "dep",             STATE_ENHANCESENTRY, 0 },
+  { STATE_FRESHENS,    "dep",             STATE_FRESHENSENTRY, 0 },
+  { NUMSTATES }
+
+};
+
+// Deps are stored as offsets into source->idarraydata
+typedef struct _deps {
+  Offset provides;
+  Offset requires;
+  Offset obsoletes;
+  Offset conflicts;
+  Offset recommends;
+  Offset supplements;
+  Offset enhances;
+  Offset suggests;
+  Offset freshens;
+} Deps;
+
+/*
+ * parser data
+ */
+
+typedef struct _parsedata {
+  // XML parser data
+  int depth;
+  enum state state;    // current state
+  int statedepth;
+  char *content;       // buffer for content of node
+  int lcontent;                // actual length of current content
+  int acontent;                // actual buffer size
+  int docontent;       // handle content
+
+  // source data
+  int pack;             // number of solvables
+
+  Pool *pool;          // current pool
+  Source *source;      // current source
+  Solvable *start;      // collected solvables
+
+  // all dependencies
+  Deps *deps;           // dependencies array, indexed by pack#
+
+  // package data
+  int  epoch;          // epoch (as offset into evrspace)
+  int  version;                // version (as offset into evrspace)
+  int  release;                // release (as offset into evrspace)
+  char *evrspace;      // buffer for evr
+  int  aevrspace;      // actual buffer space
+  int  levrspace;      // actual evr length
+  char *kind;
+
+  struct stateswitch *swtab[NUMSTATES];
+  enum state sbtab[NUMSTATES];
+} Parsedata;
+
+
+/*------------------------------------------------------------------*/
+/* E:V-R handling */
+
+// create Id from epoch:version-release
+
+static Id
+evr2id(Pool *pool, Parsedata *pd, const char *e, const char *v, const char *r)
+{
+  char *c;
+  int l;
+
+  // treat explitcit 0 as NULL
+  if (e && !strcmp(e, "0"))
+    e = NULL;
+
+  if (v && !e)
+    {
+      const char *v2;
+      // scan version for ":"
+      for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)     // skip leading digits
+        ;
+      // if version contains ":", set epoch to "0"
+      if (v2 > v && *v2 == ':')
+       e = "0";
+    }
+  
+  // compute length of Id string
+  l = 1;  // for the \0
+  if (e)
+    l += strlen(e) + 1;  // e:
+  if (v)
+    l += strlen(v);      // v
+  if (r)
+    l += strlen(r) + 1;  // -r
+
+  // extend content if not sufficient
+  if (l > pd->acontent)
+    {
+      pd->content = (char *)realloc(pd->content, l + 256);
+      pd->acontent = l + 256;
+    }
+
+  // copy e-v-r to content
+  c = pd->content;
+  if (e)
+    {
+      strcpy(c, e);
+      c += strlen(c);
+      *c++ = ':';
+    }
+  if (v)
+    {
+      strcpy(c, v);
+      c += strlen(c);
+    }
+  if (r)
+    {
+      *c++ = '-';
+      strcpy(c, r);
+      c += strlen(c);
+    }
+  *c = 0;
+  // if nothing inserted, return Id 0
+  if (!*pd->content)
+    return ID_NULL;
+#if 0
+  fprintf(stderr, "evr: %s\n", pd->content);
+#endif
+  // intern and create
+  return str2id(pool, pd->content, 1);
+}
+
+
+// create e:v-r from attributes
+// atts is array of name,value pairs, NULL at end
+//   even index into atts is name
+//   odd index is value
+//
+static Id
+evr_atts2id(Pool *pool, Parsedata *pd, const char **atts)
+{
+  const char *e, *v, *r;
+  e = v = r = 0;
+  for (; *atts; atts += 2)
+    {
+      if (!strcmp(*atts, "epoch"))
+       e = atts[1];
+      else if (!strcmp(*atts, "version"))
+       v = atts[1];
+      else if (!strcmp(*atts, "release"))
+       r = atts[1];
+    }
+  return evr2id(pool, pd, e, v, r);
+}
+
+/*------------------------------------------------------------------*/
+/* rel operator handling */
+
+struct flagtab {
+  char *from;
+  int to;
+};
+
+static struct flagtab flagtab[] = {
+  { ">",  REL_GT },
+  { "=",  REL_EQ },
+  { ">=", REL_GT|REL_EQ },
+  { "<",  REL_LT },
+  { "!=", REL_GT|REL_LT },
+  { "<=", REL_LT|REL_EQ },
+  { "(any)", REL_LT|REL_EQ|REL_GT },
+  { "==", REL_EQ },
+  { "gt", REL_GT },
+  { "eq", REL_EQ },
+  { "ge", REL_GT|REL_EQ },
+  { "lt", REL_LT },
+  { "ne", REL_GT|REL_LT },
+  { "le", REL_LT|REL_EQ },
+  { "gte", REL_GT|REL_EQ },
+  { "lte", REL_LT|REL_EQ },
+  { "GT", REL_GT },
+  { "EQ", REL_EQ },
+  { "GE", REL_GT|REL_EQ },
+  { "LT", REL_LT },
+  { "NE", REL_GT|REL_LT },
+  { "LE", REL_LT|REL_EQ }
+};
+
+/*
+ * process new dependency from parser
+ *  olddeps = already collected deps, this defines the 'kind' of dep
+ *  atts = array of name,value attributes of dep
+ *  isreq == 1 if its a requires
+ */
+
+static unsigned int
+adddep(Pool *pool, Parsedata *pd, unsigned int olddeps, const char **atts, int isreq)
+{
+  Id id, name;
+  const char *n, *f, *k;
+  const char **a;
+
+  n = f = k = NULL;
+
+  /* loop over name,value pairs */
+  for (a = atts; *a; a += 2)
+    {
+      if (!strcmp(*a, "name"))
+       n = a[1];
+      if (!strcmp(*a, "kind"))
+       k = a[1];
+      else if (!strcmp(*a, "op"))
+       f = a[1];
+      else if (isreq && !strcmp(*a, "pre") && a[1][0] == '1')
+        isreq = 2;
+    }
+  if (!n)                             /* quit if no name found */
+    return olddeps;
+
+  /* kind, name */
+  if (k && !strcmp(k, "package"))
+    k = NULL;                         /* package is default */
+
+  if (k)                              /* if kind!=package, intern <kind>:<name> */
+    {
+      int l = strlen(k) + 1 + strlen(n) + 1;
+      if (l > pd->acontent)           /* extend buffer if needed */
+       {
+         pd->content = (char *)realloc(pd->content, l + 256);
+         pd->acontent = l + 256;
+       }
+      sprintf(pd->content, "%s:%s", k, n);
+      name = str2id(pool, pd->content, 1);
+    }
+  else
+    name = str2id(pool, n, 1);       /* package: just intern <name> */
+
+  if (f)                              /* operator ? */
+    {
+      /* intern e:v-r */
+      Id evr = evr_atts2id(pool, pd, atts);
+      /* parser operator to flags */
+      int flags;
+      for (flags = 0; flags < sizeof(flagtab)/sizeof(*flagtab); flags++)
+       if (!strcmp(f, flagtab[flags].from))
+         {
+           flags = flagtab[flags].to;
+           break;
+         }
+      if (flags > 7)
+       flags = 0;
+      /* intern rel */
+      id = rel2id(pool, name, evr, flags, 1);
+    }
+  else
+    id = name;                        /* no operator */
+
+  /* add new dependency to source */
+  return source_addid_dep(pd->source, olddeps, id, isreq);
+}
+
+
+/*----------------------------------------------------------------*/
+
+/*
+ * XML callback
+ * <name>
+ * 
+ */
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+  Parsedata *pd = (Parsedata *)userData;
+  struct stateswitch *sw;
+  Pool *pool = pd->pool;
+
+  if (pd->depth != pd->statedepth)
+    {
+      pd->depth++;
+      return;
+    }
+
+  /* ignore deps element */
+  if (pd->state == STATE_PACKAGE && !strcmp(name, "deps"))
+    return;
+
+  pd->depth++;
+
+  /* find node name in stateswitch */
+  for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
+  {
+    if (!strcmp(sw->ename, name))
+      break;
+  }
+
+  /* check if we're at the right level */
+  if (sw->from != pd->state)
+    {
+#if 0
+      fprintf(stderr, "into unknown: %s\n", name);
+#endif
+      return;
+    }
+  
+  // set new state
+  pd->state = sw->to;
+
+  pd->docontent = sw->docontent;
+  pd->statedepth = pd->depth;
+
+  // start with empty content
+  // (will collect data until end element
+  pd->lcontent = 0;
+  *pd->content = 0;
+
+  switch (pd->state)
+    {
+
+    case STATE_NAME:
+      if (pd->kind)                   /* if kind is set (non package) */
+        {
+          strcpy(pd->content, pd->kind);
+          pd->lcontent = strlen(pd->content);
+         pd->content[pd->lcontent++] = ':';   /* prefix name with '<kind>:' */
+         pd->content[pd->lcontent] = 0;
+       }
+      break;
+
+    case STATE_SUBCHANNEL:
+      pd->pack = 0;
+      break;
+
+    case STATE_PACKAGE:                       /* solvable name */
+
+      if ((pd->pack & PACK_BLOCK) == 0)  /* alloc new block ? */
+       {
+         pool->solvables = (Solvable *)realloc(pool->solvables, (pool->nsolvables + pd->pack + PACK_BLOCK + 1) * sizeof(Solvable));
+         pd->start = pool->solvables + pd->source->start;
+          memset(pd->start + pd->pack, 0, (PACK_BLOCK + 1) * sizeof(Solvable));
+         if (!pd->deps)
+           pd->deps = (Deps *)malloc((pd->pack + PACK_BLOCK + 1) * sizeof(Deps));
+         else
+           pd->deps = (Deps *)realloc(pd->deps, (pd->pack + PACK_BLOCK + 1) * sizeof(Deps));
+          memset(pd->deps + pd->pack, 0, (PACK_BLOCK + 1) * sizeof(Deps));
+       }
+
+      if (!strcmp(name, "selection"))
+        pd->kind = "selection";
+      else if (!strcmp(name, "pattern"))
+        pd->kind = "pattern";
+      else if (!strcmp(name, "atom"))
+        pd->kind = "atom";
+      else if (!strcmp(name, "patch"))
+        pd->kind = "patch";
+      else
+        pd->kind = NULL;              /* default is package */
+      pd->levrspace = 1;
+      pd->epoch = 0;
+      pd->version = 0;
+      pd->release = 0;
+#if 0
+      fprintf(stderr, "package #%d\n", pd->pack);
+#endif
+      break;
+
+    case STATE_UPDATE:
+      pd->levrspace = 1;
+      pd->epoch = 0;
+      pd->version = 0;
+      pd->release = 0;
+      break;
+
+    case STATE_PROVIDES:              /* start of provides */
+      pd->deps[pd->pack].provides = 0;
+      break;
+    case STATE_PROVIDESENTRY:         /* entry within provides */
+      pd->deps[pd->pack].provides = adddep(pool, pd, pd->deps[pd->pack].provides, atts, 0);
+      break;
+    case STATE_REQUIRES:
+      pd->deps[pd->pack].requires = 0;
+      break;
+    case STATE_REQUIRESENTRY:
+      pd->deps[pd->pack].requires = adddep(pool, pd, pd->deps[pd->pack].requires, atts, 1);
+      break;
+    case STATE_OBSOLETES:
+      pd->deps[pd->pack].obsoletes = 0;
+      break;
+    case STATE_OBSOLETESENTRY:
+      pd->deps[pd->pack].obsoletes = adddep(pool, pd, pd->deps[pd->pack].obsoletes, atts, 0);
+      break;
+    case STATE_CONFLICTS:
+      pd->deps[pd->pack].conflicts = 0;
+      break;
+    case STATE_CONFLICTSENTRY:
+      pd->deps[pd->pack].conflicts = adddep(pool, pd, pd->deps[pd->pack].conflicts, atts, 0);
+      break;
+    case STATE_RECOMMENDS:
+      pd->deps[pd->pack].recommends = 0;
+      break;
+    case STATE_RECOMMENDSENTRY:
+      pd->deps[pd->pack].recommends = adddep(pool, pd, pd->deps[pd->pack].recommends, atts, 0);
+      break;
+    case STATE_SUPPLEMENTS:
+      pd->deps[pd->pack].supplements= 0;
+      break;
+    case STATE_SUPPLEMENTSENTRY:
+      pd->deps[pd->pack].supplements = adddep(pool, pd, pd->deps[pd->pack].supplements, atts, 0);
+      break;
+    case STATE_SUGGESTS:
+      pd->deps[pd->pack].suggests = 0;
+      break;
+    case STATE_SUGGESTSENTRY:
+      pd->deps[pd->pack].suggests = adddep(pool, pd, pd->deps[pd->pack].suggests, atts, 0);
+      break;
+    case STATE_ENHANCES:
+      pd->deps[pd->pack].enhances = 0;
+      break;
+    case STATE_ENHANCESENTRY:
+      pd->deps[pd->pack].enhances = adddep(pool, pd, pd->deps[pd->pack].enhances, atts, 0);
+      break;
+    case STATE_FRESHENS:
+      pd->deps[pd->pack].freshens = 0;
+      break;
+    case STATE_FRESHENSENTRY:
+      pd->deps[pd->pack].freshens = adddep(pool, pd, pd->deps[pd->pack].freshens, atts, 0);
+      break;
+    default:
+      break;
+    }
+}
+
+
+/*
+ * XML callback
+ * </name>
+ * 
+ * create Solvable from collected data
+ */
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+  Parsedata *pd = (Parsedata *)userData;
+  Pool *pool = pd->pool;
+  Solvable *s = pd->start ? pd->start + pd->pack : NULL;
+  Id evr;
+
+  if (pd->depth != pd->statedepth)
+    {
+      pd->depth--;
+      // printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+      return;
+    }
+
+  /* ignore deps element */
+  if (pd->state == STATE_PACKAGE && !strcmp(name, "deps"))
+    return;
+
+  pd->depth--;
+  pd->statedepth--;
+  switch (pd->state)
+    {
+
+    case STATE_PACKAGE:                       /* package complete */
+
+      if (!s->arch)                    /* default to "noarch" */
+       s->arch = ARCH_NOARCH;
+
+      if (!s->evr && pd->version)      /* set solvable evr */
+        s->evr = evr2id(pool, pd,
+                        pd->epoch   ? pd->evrspace + pd->epoch   : 0,
+                        pd->version ? pd->evrspace + pd->version : 0,
+                        pd->release ? pd->evrspace + pd->release : 0);
+      /* ensure self-provides */
+      if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+        {
+          pd->deps[pd->pack].provides = source_addid_dep(pd->source, pd->deps[pd->pack].provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+        }
+      pd->pack++;                     /* inc pack count */
+      break;
+    case STATE_NAME:
+      s->name = str2id(pool, pd->content, 1);
+      break;
+    case STATE_UPDATE:                /* new version, keeping all other metadata */
+      evr = evr2id(pool, pd,
+                   pd->epoch   ? pd->evrspace + pd->epoch   : 0,
+                   pd->version ? pd->evrspace + pd->version : 0,
+                   pd->release ? pd->evrspace + pd->release : 0);
+      pd->levrspace = 1;
+      pd->epoch = 0;
+      pd->version = 0;
+      pd->release = 0;
+      /* use highest evr */
+      if (!s->evr || evrcmp(pool, s->evr, evr) <= 0)
+       s->evr = evr;
+      break;
+    case STATE_EPOCH:
+    case STATE_VERSION:
+    case STATE_RELEASE:
+    case STATE_PEPOCH:
+    case STATE_PVERSION:
+    case STATE_PRELEASE:
+      /* ensure buffer space */
+      if (pd->lcontent + 1 + pd->levrspace > pd->aevrspace)
+       {
+         pd->evrspace = (char *)realloc(pd->evrspace, pd->lcontent + 1 + pd->levrspace + 256);
+         pd->aevrspace = pd->lcontent + 1 + pd->levrspace + 256;
+       }
+      memcpy(pd->evrspace + pd->levrspace, pd->content, pd->lcontent + 1);
+      if (pd->state == STATE_EPOCH || pd->state == STATE_PEPOCH)
+       pd->epoch = pd->levrspace;
+      else if (pd->state == STATE_VERSION || pd->state == STATE_PVERSION)
+       pd->version = pd->levrspace;
+      else
+       pd->release = pd->levrspace;
+      pd->levrspace += pd->lcontent + 1;
+      break;
+    case STATE_ARCH:
+    case STATE_PARCH:
+      s->arch = str2id(pool, pd->content, 1);
+      break;
+    default:
+      break;
+    }
+  pd->state = pd->sbtab[pd->state];
+  pd->docontent = 0;
+  // printf("back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+}
+
+
+/*
+ * XML callback
+ * character data
+ * 
+ */
+
+static void XMLCALL
+characterData(void *userData, const XML_Char *s, int len)
+{
+  Parsedata *pd = (Parsedata *)userData;
+  int l;
+  char *c;
+
+  // check if current nodes content is interesting
+  if (!pd->docontent)
+    return;
+
+  // adapt content buffer
+  l = pd->lcontent + len + 1;
+  if (l > pd->acontent)
+    {
+      pd->content = (char *)realloc(pd->content, l + 256);
+      pd->acontent = l + 256;
+    }
+  // append new content to buffer
+  c = pd->content + pd->lcontent;
+  pd->lcontent += len;
+  while (len-- > 0)
+    *c++ = *s++;
+  *c = 0;
+}
+
+/*-------------------------------------------------------------------*/
+
+#define BUFF_SIZE 8192
+
+/*
+ * read 'helix' type xml from fp
+ * add packages to pool/source
+ * 
+ */
+
+Source *
+pool_addsource_helix(Pool *pool, FILE *fp)
+{
+  Parsedata pd;
+  char buf[BUFF_SIZE];
+  int i, l;
+  Source *source;
+  Solvable *solvable;
+  Deps *deps;
+  struct stateswitch *sw;
+
+  // create empty source
+  source = pool_addsource_empty(pool);
+
+  // prepare parsedata
+  memset(&pd, 0, sizeof(pd));
+  for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
+    {
+      if (!pd.swtab[sw->from])
+        pd.swtab[sw->from] = sw;
+      pd.sbtab[sw->to] = sw->from;
+    }
+
+  pd.pool = pool;
+  pd.source = source;
+
+  pd.content = (char *)malloc(256);    /* must hold all solvable kinds! */
+  pd.acontent = 256;
+  pd.lcontent = 0;
+
+  pd.evrspace = (char *)malloc(256);
+  pd.aevrspace= 256;
+  pd.levrspace = 1;
+
+  // set up XML parser
+
+  XML_Parser parser = XML_ParserCreate(NULL);
+  XML_SetUserData(parser, &pd);       /* make parserdata available to XML callbacks */
+  XML_SetElementHandler(parser, startElement, endElement);
+  XML_SetCharacterDataHandler(parser, characterData);
+
+  // read/parse XML file
+  for (;;)
+    {
+      l = fread(buf, 1, sizeof(buf), fp);
+      if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
+       {
+         fprintf(stderr, "%s at line %u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
+         exit(1);
+       }
+      if (l == 0)
+       break;
+    }
+  XML_ParserFree(parser);
+
+  // adapt package count
+  pool->nsolvables += pd.pack;
+  source->nsolvables = pd.pack;
+
+  // now set dependency pointers for each solvable
+  deps = pd.deps;
+  solvable = pool->solvables + source->start;
+  for (i = 0; i < pd.pack; i++, solvable++)
+    {
+      if (deps[i].provides)
+        solvable->provides = source->idarraydata + deps[i].provides;
+      if (deps[i].requires)
+        solvable->requires = source->idarraydata + deps[i].requires;
+      if (deps[i].conflicts)
+        solvable->conflicts = source->idarraydata + deps[i].conflicts;
+      if (deps[i].obsoletes)
+        solvable->obsoletes = source->idarraydata + deps[i].obsoletes;
+      if (deps[i].recommends)
+        solvable->recommends = source->idarraydata + deps[i].recommends;
+      if (deps[i].supplements)
+        solvable->supplements = source->idarraydata + deps[i].supplements;
+      if (deps[i].suggests)
+        solvable->suggests = source->idarraydata + deps[i].suggests;
+      if (deps[i].enhances)
+        solvable->enhances = source->idarraydata + deps[i].enhances;
+      if (deps[i].freshens)
+        solvable->freshens = source->idarraydata + deps[i].freshens;
+    }
+
+  free(deps);
+  free(pd.content);
+  free(pd.evrspace);
+
+  return source;
+}
diff --git a/tools/source_helix.h b/tools/source_helix.h
new file mode 100644 (file)
index 0000000..dfcf45e
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * source_helix.h
+ * 
+ */
+
+#ifndef SOURCE_HELIX_H
+#define SOURCE_HELIX_H
+
+#include <stdio.h>
+#include "pool.h"
+#include "source.h"
+
+extern Source *pool_addsource_helix(Pool *pool, FILE *fp);
+
+#endif /* SOURCE_HELIX_H */
diff --git a/tools/source_patchxml.c b/tools/source_patchxml.c
new file mode 100644 (file)
index 0000000..413b061
--- /dev/null
@@ -0,0 +1,523 @@
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <expat.h>
+
+#include "pool.h"
+#include "source_patchxml.h"
+#include "source_rpmmd.h"
+
+
+enum state {
+  STATE_START,
+  STATE_PATCH,
+  STATE_ATOM,
+  STATE_NAME,
+  STATE_ARCH,
+  STATE_VERSION,
+  STATE_REQUIRES,
+  STATE_REQUIRESENTRY,
+  STATE_PROVIDES,
+  STATE_PROVIDESENTRY,
+  STATE_OBSOLETES,
+  STATE_OBSOLETESENTRY,
+  STATE_CONFLICTS,
+  STATE_CONFLICTSENTRY,
+  STATE_RECOMMENDS,
+  STATE_RECOMMENDSENTRY,
+  STATE_SUPPLEMENTS,
+  STATE_SUPPLEMENTSENTRY,
+  STATE_SUGGESTS,
+  STATE_SUGGESTSENTRY,
+  STATE_ENHANCES,
+  STATE_ENHANCESENTRY,
+  STATE_FRESHENS,
+  STATE_FRESHENSENTRY,
+  NUMSTATES
+};
+
+#define PACK_BLOCK 255
+
+
+struct stateswitch {
+  enum state from;
+  char *ename;
+  enum state to;
+  int docontent;
+};
+
+static struct stateswitch stateswitches[] = {
+  { STATE_START,       "patch",           STATE_PATCH, 0 },
+  { STATE_START,       "package",         STATE_ATOM, 0 },
+  { STATE_PATCH,       "yum:name",        STATE_NAME, 1 },
+  { STATE_PATCH,       "yum:arch",        STATE_ARCH, 1 },
+  { STATE_PATCH,       "yum:version",     STATE_VERSION, 0 },
+  { STATE_PATCH,       "name",            STATE_NAME, 1 },
+  { STATE_PATCH,       "arch",            STATE_ARCH, 1 },
+  { STATE_PATCH,       "version",         STATE_VERSION, 0 },
+  { STATE_PATCH,       "rpm:requires",    STATE_REQUIRES, 0 },
+  { STATE_PATCH,       "rpm:provides",    STATE_PROVIDES, 0 },
+  { STATE_PATCH,       "rpm:requires",    STATE_REQUIRES, 0 },
+  { STATE_PATCH,       "rpm:obsoletes",   STATE_OBSOLETES , 0 },
+  { STATE_PATCH,       "rpm:conflicts",   STATE_CONFLICTS , 0 },
+  { STATE_PATCH,       "rpm:recommends" , STATE_RECOMMENDS , 0 },
+  { STATE_PATCH,       "rpm:supplements", STATE_SUPPLEMENTS, 0 },
+  { STATE_PATCH,       "rpm:suggests",    STATE_SUGGESTS, 0 },
+  { STATE_PATCH,       "rpm:enhances",    STATE_ENHANCES, 0 },
+  { STATE_PATCH,       "rpm:freshens",    STATE_FRESHENS, 0 },
+  { STATE_PATCH,       "suse:freshens",   STATE_FRESHENS, 0 },
+  { STATE_PATCH,       "atoms",          STATE_START, 0 },
+  { STATE_PROVIDES,    "rpm:entry",       STATE_PROVIDESENTRY, 0 },
+  { STATE_REQUIRES,    "rpm:entry",       STATE_REQUIRESENTRY, 0 },
+  { STATE_OBSOLETES,   "rpm:entry",       STATE_OBSOLETESENTRY, 0 },
+  { STATE_CONFLICTS,   "rpm:entry",       STATE_CONFLICTSENTRY, 0 },
+  { STATE_RECOMMENDS,  "rpm:entry",       STATE_RECOMMENDSENTRY, 0 },
+  { STATE_SUPPLEMENTS, "rpm:entry",       STATE_SUPPLEMENTSENTRY, 0 },
+  { STATE_SUGGESTS,    "rpm:entry",       STATE_SUGGESTSENTRY, 0 },
+  { STATE_ENHANCES,    "rpm:entry",       STATE_ENHANCESENTRY, 0 },
+  { STATE_FRESHENS,    "rpm:entry",       STATE_FRESHENSENTRY, 0 },
+  { STATE_FRESHENS,    "suse:entry",      STATE_FRESHENSENTRY, 0 },
+  { NUMSTATES}
+};
+
+struct deps {
+  unsigned int provides;
+  unsigned int requires;
+  unsigned int obsoletes;
+  unsigned int conflicts;
+  unsigned int recommends;
+  unsigned int supplements;
+  unsigned int enhances;
+  unsigned int suggests;
+  unsigned int freshens;
+};
+
+struct parsedata {
+  int depth;
+  enum state state;
+  int statedepth;
+  char *content;
+  int lcontent;
+  int acontent;
+  int docontent;
+  int pack;
+  Pool *pool;
+  Source *source;
+  Solvable *start;
+  struct deps *deps;
+  char *kind;
+
+  struct stateswitch *swtab[NUMSTATES];
+  enum state sbtab[NUMSTATES];
+};
+
+static Id
+makeevr_atts(Pool *pool, struct parsedata *pd, const char **atts)
+{
+  const char *e, *v, *r, *v2;
+  char *c;
+  int l;
+
+  e = v = r = 0;
+  for (; *atts; atts += 2)
+    {
+      if (!strcmp(*atts, "epoch"))
+       e = atts[1];
+      else if (!strcmp(*atts, "ver"))
+       v = atts[1];
+      else if (!strcmp(*atts, "rel"))
+       r = atts[1];
+    }
+  if (e && !strcmp(e, "0"))
+    e = 0;
+  if (v && !e)
+    {
+      for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)
+        ;
+      if (v2 > v && *v2 == ':')
+       e = "0";
+    }
+  l = 1;
+  if (e)
+    l += strlen(e) + 1;
+  if (v)
+    l += strlen(v);
+  if (r)
+    l += strlen(r) + 1;
+  if (l > pd->acontent)
+    {
+      pd->content = realloc(pd->content, l + 256);
+      pd->acontent = l + 256;
+    }
+  c = pd->content;
+  if (e)
+    {
+      strcpy(c, e);
+      c += strlen(c);
+      *c++ = ':';
+    }
+  if (v)
+    {
+      strcpy(c, v);
+      c += strlen(c);
+    }
+  if (r)
+    {
+      *c++ = '-';
+      strcpy(c, r);
+      c += strlen(c);
+    }
+  *c = 0;
+  if (!*pd->content)
+    return 0;
+#if 0
+  fprintf(stderr, "evr: %s\n", pd->content);
+#endif
+  return str2id(pool, pd->content, 1);
+}
+
+static char *flagtab[] = {
+  "GT",
+  "EQ",
+  "GE",
+  "LT",
+  "NE",
+  "LE"
+};
+
+static unsigned int
+adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts, int isreq)
+{
+  Id id, name;
+  const char *n, *f, *k;
+  const char **a;
+
+  n = f = k = 0;
+  for (a = atts; *a; a += 2)
+    {
+      if (!strcmp(*a, "name"))
+       n = a[1];
+      else if (!strcmp(*a, "flags"))
+       f = a[1];
+      else if (!strcmp(*a, "kind"))
+       k = a[1];
+      else if (isreq && !strcmp(*a, "pre") && a[1][0] == '1')
+       isreq = 2;
+    }
+  if (!n)
+    return olddeps;
+  if (k && !strcmp(k, "package"))
+    k = 0;
+  if (k)
+    {
+      int l = strlen(k) + 1 + strlen(n) + 1;
+      if (l > pd->acontent)
+       {
+         pd->content = realloc(pd->content, l + 256);
+         pd->acontent = l + 256;
+       }
+      sprintf(pd->content, "%s:%s", k, n); 
+      name = str2id(pool, pd->content, 1); 
+    }
+  else
+    name = str2id(pool, (char *)n, 1);
+  if (f)
+    {
+      Id evr = makeevr_atts(pool, pd, atts);
+      int flags;
+      for (flags = 0; flags < 6; flags++)
+       if (!strcmp(f, flagtab[flags]))
+         break;
+      flags = flags < 6 ? flags + 1 : 0;
+      id = rel2id(pool, name, evr, flags, 1);
+    }
+  else
+    id = name;
+#if 0
+  fprintf(stderr, "new dep %s%s%s\n", id2str(pool, d), id2rel(pool, d), id2evr(pool, d));
+#endif
+  return source_addid_dep(pd->source, olddeps, id, isreq);
+}
+
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+  struct parsedata *pd = userData;
+  Pool *pool = pd->pool;
+  Solvable *s = pd->start ? pd->start + pd->pack : 0;
+  struct stateswitch *sw;
+
+  if (pd->depth != pd->statedepth)
+    {
+      pd->depth++;
+      return;
+    }
+
+  if (pd->state == STATE_PATCH && !strcmp(name, "format"))
+    return;
+
+  pd->depth++;
+  for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
+    if (!strcmp(sw->ename, name))
+      break;
+  if (sw->from != pd->state)
+    {
+#if 0
+      fprintf(stderr, "into unknown: %s\n", name);
+#endif
+      return;
+    }
+  pd->state = sw->to;
+  pd->docontent = sw->docontent;
+  pd->statedepth = pd->depth;
+  pd->lcontent = 0;
+  *pd->content = 0;
+  switch(pd->state)
+    {
+    case STATE_NAME:
+      if (pd->kind)
+        {
+          strcpy(pd->content, pd->kind);
+          pd->lcontent = strlen(pd->content);
+          pd->content[pd->lcontent++] = ':';
+          pd->content[pd->lcontent] = 0;
+        }
+      break;
+    case STATE_PATCH:
+    case STATE_ATOM:
+      if (pd->state == STATE_ATOM)
+       {
+         /* HACK: close patch */
+         if (pd->kind && !strcmp(pd->kind, "patch"))
+           {
+             if (!s->arch)
+               s->arch = ARCH_NOARCH;
+             pd->deps[pd->pack].provides = source_addid_dep(pd->source, pd->deps[pd->pack].provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+             pd->pack++;
+           }
+         pd->kind = "atom";
+         pd->state = STATE_PATCH;
+       }
+      else
+        pd->kind = "patch";
+      if ((pd->pack & PACK_BLOCK) == 0)
+        {
+          pool->solvables = realloc(pool->solvables, (pool->nsolvables + pd->pack + PACK_BLOCK + 1) * sizeof(Solvable));
+          pd->start = pool->solvables + pd->source->start;
+          memset(pd->start + pd->pack, 0, (PACK_BLOCK + 1) * sizeof(Solvable));
+          if (!pd->deps)
+            pd->deps = malloc((pd->pack + PACK_BLOCK + 1) * sizeof(struct deps));
+          else
+            pd->deps = realloc(pd->deps, (pd->pack + PACK_BLOCK + 1) * sizeof(struct deps));
+          memset(pd->deps + pd->pack, 0, (PACK_BLOCK + 1) * sizeof(struct deps));
+        }
+#if 0
+      fprintf(stderr, "package #%d\n", pd->pack);
+#endif
+      break;
+    case STATE_VERSION:
+      s->evr = makeevr_atts(pool, pd, atts);
+      break;
+    case STATE_PROVIDES:
+      pd->deps[pd->pack].provides = 0;
+      break;
+    case STATE_PROVIDESENTRY:
+      pd->deps[pd->pack].provides = adddep(pool, pd, pd->deps[pd->pack].provides, atts, 0);
+      break;
+    case STATE_REQUIRES:
+      pd->deps[pd->pack].requires = 0;
+      break;
+    case STATE_REQUIRESENTRY:
+      pd->deps[pd->pack].requires = adddep(pool, pd, pd->deps[pd->pack].requires, atts, 1);
+      break;
+    case STATE_OBSOLETES:
+      pd->deps[pd->pack].obsoletes = 0;
+      break;
+    case STATE_OBSOLETESENTRY:
+      pd->deps[pd->pack].obsoletes = adddep(pool, pd, pd->deps[pd->pack].obsoletes, atts, 0);
+      break;
+    case STATE_CONFLICTS:
+      pd->deps[pd->pack].conflicts = 0;
+      break;
+    case STATE_CONFLICTSENTRY:
+      pd->deps[pd->pack].conflicts = adddep(pool, pd, pd->deps[pd->pack].conflicts, atts, 0);
+      break;
+    case STATE_RECOMMENDS:
+      pd->deps[pd->pack].recommends = 0;
+      break;
+    case STATE_RECOMMENDSENTRY:
+      pd->deps[pd->pack].recommends = adddep(pool, pd, pd->deps[pd->pack].recommends, atts, 0);
+      break;
+    case STATE_SUPPLEMENTS:
+      pd->deps[pd->pack].supplements= 0;
+      break;
+    case STATE_SUPPLEMENTSENTRY:
+      pd->deps[pd->pack].supplements = adddep(pool, pd, pd->deps[pd->pack].supplements, atts, 0);
+      break;
+    case STATE_SUGGESTS:
+      pd->deps[pd->pack].suggests = 0;
+      break;
+    case STATE_SUGGESTSENTRY:
+      pd->deps[pd->pack].suggests = adddep(pool, pd, pd->deps[pd->pack].suggests, atts, 0);
+      break;
+    case STATE_ENHANCES:
+      pd->deps[pd->pack].enhances = 0;
+      break;
+    case STATE_ENHANCESENTRY:
+      pd->deps[pd->pack].enhances = adddep(pool, pd, pd->deps[pd->pack].enhances, atts, 0);
+      break;
+    case STATE_FRESHENS:
+      pd->deps[pd->pack].freshens = 0;
+      break;
+    case STATE_FRESHENSENTRY:
+      pd->deps[pd->pack].freshens = adddep(pool, pd, pd->deps[pd->pack].freshens, atts, 0);
+      break;
+    default:
+      break;
+    }
+}
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+  struct parsedata *pd = userData;
+  Pool *pool = pd->pool;
+  Solvable *s = pd->start ? pd->start + pd->pack : 0;
+
+  if (pd->depth != pd->statedepth)
+    {
+      pd->depth--;
+      // printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+      return;
+    }
+
+  if (pd->state == STATE_PATCH && !strcmp(name, "format"))
+    return;
+
+  pd->depth--;
+  pd->statedepth--;
+  switch (pd->state)
+    {
+    case STATE_PATCH:
+      if (!strcmp(name, "patch") && strcmp(pd->kind, "patch"))
+       break;  /* already closed */
+      if (!s->arch)
+       s->arch = ARCH_NOARCH;
+      if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+        pd->deps[pd->pack].provides = source_addid_dep(pd->source, pd->deps[pd->pack].provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+      pd->pack++;
+      break;
+    case STATE_NAME:
+      s->name = str2id(pool, pd->content, 1);
+      break;
+    case STATE_ARCH:
+      s->arch = str2id(pool, pd->content, 1);
+      break;
+    default:
+      break;
+    }
+  pd->state = pd->sbtab[pd->state];
+  pd->docontent = 0;
+  // printf("back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+}
+
+static void XMLCALL
+characterData(void *userData, const XML_Char *s, int len)
+{
+  struct parsedata *pd = userData;
+  int l;
+  char *c;
+
+  if (!pd->docontent)
+    return;
+  l = pd->lcontent + len + 1;
+  if (l > pd->acontent)
+    {
+      pd->content = realloc(pd->content, l + 256);
+      pd->acontent = l + 256;
+    }
+  c = pd->content + pd->lcontent;
+  pd->lcontent += len;
+  while (len-- > 0)
+    *c++ = *s++;
+  *c = 0;
+}
+
+
+#define BUFF_SIZE 8192
+
+Source *
+pool_addsource_patchxml(Pool *pool, FILE *fp)
+{
+  struct parsedata pd;
+  char buf[BUFF_SIZE];
+  int i, l;
+  Source *source;
+  Solvable *s;
+  struct deps *deps;
+  struct stateswitch *sw;
+
+  source = pool_addsource_empty(pool);
+  memset(&pd, 0, sizeof(pd));
+  for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
+    {
+      if (!pd.swtab[sw->from])
+        pd.swtab[sw->from] = sw;
+      pd.sbtab[sw->to] = sw->from;
+    }
+  pd.pool = pool;
+  pd.source = source;
+  pd.content = malloc(256);
+  pd.acontent = 256;
+  pd.lcontent = 0;
+  XML_Parser parser = XML_ParserCreate(NULL);
+  XML_SetUserData(parser, &pd);
+  XML_SetElementHandler(parser, startElement, endElement);
+  XML_SetCharacterDataHandler(parser, characterData);
+  for (;;)
+    {
+      l = fread(buf, 1, sizeof(buf), fp);
+      if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
+       {
+         fprintf(stderr, "%s at line %u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
+         exit(1);
+       }
+      if (l == 0)
+       break;
+    }
+  XML_ParserFree(parser);
+
+  pool->nsolvables += pd.pack;
+  source->nsolvables = pd.pack;
+
+  deps = pd.deps;
+  s = pool->solvables + source->start;
+  for (i = 0; i < pd.pack; i++, s++)
+    {
+      if (deps[i].provides)
+        s->provides = source->idarraydata + deps[i].provides;
+      if (deps[i].requires)
+        s->requires = source->idarraydata + deps[i].requires;
+      if (deps[i].conflicts)
+        s->conflicts = source->idarraydata + deps[i].conflicts;
+      if (deps[i].obsoletes)
+        s->obsoletes = source->idarraydata + deps[i].obsoletes;
+      if (deps[i].recommends)
+        s->recommends = source->idarraydata + deps[i].recommends;
+      if (deps[i].supplements)
+        s->supplements = source->idarraydata + deps[i].supplements;
+      if (deps[i].suggests)
+        s->suggests = source->idarraydata + deps[i].suggests;
+      if (deps[i].enhances)
+        s->enhances = source->idarraydata + deps[i].enhances;
+      if (deps[i].freshens)
+        s->freshens = source->idarraydata + deps[i].freshens;
+    }
+  free(deps);
+  free(pd.content);
+  return source;
+}
diff --git a/tools/source_patchxml.h b/tools/source_patchxml.h
new file mode 100644 (file)
index 0000000..75d8aa3
--- /dev/null
@@ -0,0 +1 @@
+extern Source *pool_addsource_patchxml(Pool *pool, FILE *fp);
diff --git a/tools/source_rpmdb.c b/tools/source_rpmdb.c
new file mode 100644 (file)
index 0000000..b946bcb
--- /dev/null
@@ -0,0 +1,860 @@
+/*
+ * source_rpmdb
+ * 
+ * convert rpm db to source
+ * 
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <db43/db.h>
+
+#include "pool.h"
+#include "hash.h"
+#include "source_rpmdb.h"
+
+#define TAG_NAME               1000
+#define TAG_VERSION            1001
+#define TAG_RELEASE            1002
+#define TAG_EPOCH              1003
+#define TAG_SUMMARY            1004
+#define TAG_DESCRIPTION                1005
+#define TAG_BUILDTIME          1006
+#define TAG_VENDOR             1011
+#define TAG_ARCH               1022
+#define TAG_PROVIDENAME                1047
+#define TAG_REQUIREFLAGS       1048
+#define TAG_REQUIRENAME                1049
+#define TAG_REQUIREVERSION     1050
+#define TAG_CONFLICTFLAGS      1053
+#define TAG_CONFLICTNAME       1054
+#define TAG_CONFLICTVERSION    1055
+#define TAG_OBSOLETENAME       1090
+#define TAG_PROVIDEFLAGS       1112
+#define TAG_PROVIDEVERSION     1113
+#define TAG_OBSOLETEFLAGS      1114
+#define TAG_OBSOLETEVERSION    1115
+#define TAG_DIRINDEXES         1116
+#define TAG_BASENAMES          1117
+#define TAG_DIRNAMES           1118
+#define TAG_SUGGESTSNAME       1156
+#define TAG_SUGGESTSVERSION    1157
+#define TAG_SUGGESTSFLAGS      1158
+#define TAG_ENHANCESNAME       1159
+#define TAG_ENHANCESVERSION    1160
+#define TAG_ENHANCESFLAGS      1161
+
+#define DEP_LESS               (1 << 1)
+#define DEP_GREATER            (1 << 2)
+#define DEP_EQUAL              (1 << 3)
+#define DEP_STRONG             (1 << 27)
+#define DEP_PRE                        ((1 << 6) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 12))
+
+
+static DBT key;
+static DBT data;
+
+struct rpmid {
+  unsigned int dbid;
+  char *name;
+};
+
+typedef struct rpmhead {
+  int cnt;
+  int dcnt;
+  unsigned char *dp;
+  unsigned char data[1];
+} RpmHead;
+
+static unsigned int *
+headint32(RpmHead *h, int tag, int *cnt)
+{
+  unsigned int i, o, *r;
+  unsigned char *d, taga[4];
+
+  d = h->dp - 16;
+  taga[0] = tag >> 24;
+  taga[1] = tag >> 16;
+  taga[2] = tag >> 8;
+  taga[3] = tag;
+  for (i = 0; i < h->cnt; i++, d -= 16)
+    if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
+      break;
+  if (i >= h->cnt)
+    return 0;
+  if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 4)
+    return 0;
+  o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
+  i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
+  if (o + 4 * i > h->dcnt)
+    return 0;
+  d = h->dp + o;
+  r = calloc(i ? i : 1, sizeof(unsigned int));
+  if (cnt)
+    *cnt = i;
+  for (o = 0; o < i; o++, d += 4)
+    r[o] = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
+  return r;
+}
+
+static char *
+headstring(RpmHead *h, int tag)
+{
+  unsigned int i, o;
+  unsigned char *d, taga[4];
+  d = h->dp - 16;
+  taga[0] = tag >> 24;
+  taga[1] = tag >> 16;
+  taga[2] = tag >> 8;
+  taga[3] = tag;
+  for (i = 0; i < h->cnt; i++, d -= 16)
+    if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
+      break;
+  if (i >= h->cnt)
+    return 0;
+  if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 6)
+    return 0;
+  o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
+  return (char *)h->dp + o;
+}
+
+static char **
+headstringarray(RpmHead *h, int tag, int *cnt)
+{
+  unsigned int i, o;
+  unsigned char *d, taga[4];
+  char **r;
+
+  d = h->dp - 16;
+  taga[0] = tag >> 24;
+  taga[1] = tag >> 16;
+  taga[2] = tag >> 8;
+  taga[3] = tag;
+  for (i = 0; i < h->cnt; i++, d -= 16)
+    if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
+      break;
+  if (i >= h->cnt)
+    return 0;
+  if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 8)
+    return 0;
+  o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
+  i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
+  r = calloc(i ? i : 1, sizeof(char *));
+  if (cnt)
+    *cnt = i;
+  d = h->dp + o;
+  for (o = 0; o < i; o++)
+    {
+      r[o] = (char *)d;
+      if (o + 1 < i)
+        d += strlen((char *)d) + 1;
+      if (d >= h->dp + h->dcnt)
+        {
+          free(r);
+          return 0;
+        }
+    }
+  return r;
+}
+
+static char *headtoevr(RpmHead *h)
+{
+  unsigned int epoch, *epochp; 
+  char *version, *v;
+  char *release;
+  char *evr;
+  int epochcnt = 0;
+
+  version  = headstring(h, TAG_VERSION);
+  release  = headstring(h, TAG_RELEASE);
+  epochp = headint32(h, TAG_EPOCH, &epochcnt);
+  if (!version || !release)
+    {
+      fprintf(stderr, "headtoevr: bad rpm header\n");
+      exit(1);
+    }
+  for (v = version; *v >= 0 && *v <= '9'; v++)
+    ;
+  epoch = epochp && epochcnt ? *epochp : 0;
+  if (epoch || (v != version && *v == ':'))
+    {
+      char epochbuf[11];        /* 32bit decimal will fit in */
+      sprintf(epochbuf, "%u", epoch);
+      evr = malloc(strlen(epochbuf) + 1 + strlen(version) + 1 + strlen(release) + 1);
+      sprintf(evr, "%s:%s-%s", epochbuf, version, release);
+    }
+  else
+    {
+      evr = malloc(strlen(version) + 1 + strlen(release) + 1);
+      sprintf(evr, "%s-%s", version, release);
+    }
+  if (epochp)
+    free(epochp);
+  return evr;
+}
+
+static unsigned int
+makedeps(Pool *pool, Source *source, RpmHead *rpmhead, int tagn, int tagv, int tagf, int strong)
+{
+  char **n, **v;
+  unsigned int *f;
+  int i, cc, nc, vc, fc;
+  int haspre = 0;
+  unsigned int olddeps;
+  Id *ida;
+
+  n = headstringarray(rpmhead, tagn, &nc);
+  if (!n)
+    return 0;
+  v = headstringarray(rpmhead, tagv, &vc);
+  if (!v)
+    {
+      free(n);
+      return 0;
+    }
+  f = headint32(rpmhead, tagf, &fc);
+  if (!f)
+    {
+      free(n);
+      free(v);
+      return 0;
+    }
+  if (nc != vc || nc != fc)
+    {
+      fprintf(stderr, "bad dependency entries\n");
+      exit(1);
+    }
+
+  cc = nc;
+  if (strong)
+    {
+      cc = 0;
+      for (i = 0; i < nc; i++)
+       if ((f[i] & DEP_STRONG) == (strong == 1 ? 0 : DEP_STRONG))
+         {
+           cc++;
+           if ((f[i] & DEP_PRE) != 0)
+             haspre = 1;
+         }
+    }
+  else
+    {
+      for (i = 0; i < nc; i++)
+       if ((f[i] & DEP_PRE) != 0)
+         {
+           haspre = 1;
+           break;
+         }
+    }
+  if (tagn != TAG_REQUIRENAME)
+     haspre = 0;
+  if (cc == 0)
+    {
+      free(n);
+      free(v);
+      free(f);
+      return 0;
+    }
+  cc += haspre;
+  olddeps = source_reserve_ids(source, 0, cc);
+  ida = source->idarraydata + olddeps;
+  for (i = 0; ; i++)
+    {
+      if (i == nc)
+       {
+         if (haspre != 1)
+           break;
+         haspre = 2;
+         i = 0;
+         *ida++ = SOLVABLE_PREREQMARKER;
+       }
+      if (strong && (f[i] & DEP_STRONG) != (strong == 1 ? 0 : DEP_STRONG))
+       continue;
+      if (haspre == 1 && (f[i] & DEP_PRE) != 0)
+       continue;
+      if (haspre == 2 && (f[i] & DEP_PRE) == 0)
+       continue;
+      if (f[i] & (DEP_LESS|DEP_GREATER|DEP_EQUAL))
+       {
+         Id name, evr;
+         int flags = 0;
+         if ((f[i] & DEP_LESS) != 0)
+           flags |= 4;
+         if ((f[i] & DEP_EQUAL) != 0)
+           flags |= 2;
+         if ((f[i] & DEP_GREATER) != 0)
+           flags |= 1;
+         name = str2id(pool, n[i], 1);
+         if (v[i][0] == '0' && v[i][1] == ':' && v[i][2])
+           evr = str2id(pool, v[i] + 2, 1);
+         else
+           evr = str2id(pool, v[i], 1);
+         *ida++ = rel2id(pool, name, evr, flags, 1);
+       }
+      else
+        *ida++ = str2id(pool, n[i], 1);
+    }
+  *ida++ = 0;
+  source->idarraysize += cc + 1;
+  free(n);
+  free(v);
+  free(f);
+  return olddeps;
+}
+
+static unsigned int
+copydeps(Pool *pool, Source *source, Id *from, Pool *frompool)
+{
+  int cc;
+  Id id, *ida;
+  unsigned int olddeps;
+
+  if (!from)
+    return 0;
+  for (ida = from, cc = 0; *ida; ida++, cc++)
+    ;
+  if (cc == 0)
+    return 0;
+  olddeps = source_reserve_ids(source, 0, cc);
+  ida = source->idarraydata + olddeps;
+  if (frompool && pool != frompool)
+    {
+      while (*from)
+       {
+         id = *from++;
+         if (ISRELDEP(id))
+           {
+             Reldep *rd = GETRELDEP(frompool, id);
+             Id name = str2id(pool, id2str(frompool, rd->name), 1);
+             Id evr = str2id(pool, id2str(frompool, rd->evr), 1);
+             id = rel2id(pool, name, evr, rd->flags, 1);
+           }
+         else
+           id = str2id(pool, id2str(frompool, id), 1);
+         *ida++ = id;
+       }
+      *ida = 0;
+    }
+  else
+    memcpy(ida, from, (cc + 1) * sizeof(Id));
+  source->idarraysize += cc + 1;
+  return olddeps;
+}
+
+
+#define FILEFILTER_EXACT    0
+#define FILEFILTER_STARTS   1
+#define FILEFILTER_CONTAINS 2
+
+struct filefilter {
+  int dirmatch;
+  char *dir;
+  char *base;
+};
+
+static struct filefilter filefilters[] = {
+  { FILEFILTER_CONTAINS, "/bin/", 0},
+  { FILEFILTER_CONTAINS, "/sbin/", 0},
+  { FILEFILTER_CONTAINS, "/lib/", 0},
+  { FILEFILTER_CONTAINS, "/lib64/", 0},
+  { FILEFILTER_CONTAINS, "/etc/", 0},
+  { FILEFILTER_STARTS, "/usr/games/", 0},
+  { FILEFILTER_EXACT, "/usr/share/dict/", "words"},
+  { FILEFILTER_STARTS, "/usr/share/", "magic.mime"},
+  { FILEFILTER_STARTS, "/opt/gnome/games/", 0},
+};
+
+/* assumes last processed array is provides! */
+static unsigned int
+addfileprovides(Pool *pool, Source *source, RpmHead *rpmhead, unsigned int olddeps)
+{
+  char **bn;
+  char **dn;
+  unsigned int *di;
+  int bnc, dnc, dic;
+  int i, j;
+  struct filefilter *ff;
+  char *fn = 0;
+  int fna = 0;
+
+  bn = headstringarray(rpmhead, TAG_BASENAMES, &bnc);
+  if (!bn)
+    return olddeps;
+  dn = headstringarray(rpmhead, TAG_DIRNAMES, &dnc);
+  if (!dn)
+    {
+      free(bn);
+      return olddeps;
+    }
+  di = headint32(rpmhead, TAG_DIRINDEXES, &dic);
+  if (!di)
+    {
+      free(bn);
+      free(dn);
+      return olddeps;
+    }
+  if (bnc != dic)
+    {
+      fprintf(stderr, "bad filelist\n");
+      exit(1);
+    }
+  for (i = 0; i < bnc; i++)
+    {
+      ff = filefilters;
+      for (j = 0; j < sizeof(filefilters)/sizeof(*filefilters); j++, ff++)
+       {
+         if (ff->dir)
+           {
+             switch (ff->dirmatch)
+               {
+               case FILEFILTER_STARTS:
+                 if (strncmp(dn[di[i]], ff->dir, strlen(ff->dir)))
+                   continue;
+                 break;
+               case FILEFILTER_CONTAINS:
+                 if (!strstr(dn[di[i]], ff->dir))
+                   continue;
+                 break;
+               case FILEFILTER_EXACT:
+               default:
+                 if (strcmp(dn[di[i]], ff->dir))
+                   continue;
+                 break;
+               }
+           }
+         if (ff->base)
+           {
+             if (strcmp(bn[i], ff->base))
+               continue;
+           }
+         break;
+       }
+      if (j == sizeof(filefilters)/sizeof(*filefilters))
+       continue;
+      j = strlen(bn[i]) + strlen(dn[di[i]]) + 1;
+      if (j > fna)
+       {
+         if (fn)
+           fn = realloc(fn, j + 256);
+         else
+           fn = malloc(j + 256);
+         fna = j + 256;
+       }
+      strcpy(fn, dn[di[i]]);
+      strcat(fn, bn[i]);
+      olddeps = source_addid(source, olddeps, str2id(pool, fn, 1));
+    }
+  if (fn)
+    free(fn);
+  free(bn);
+  free(dn);
+  free(di);
+  return olddeps;
+}
+
+struct deps {
+  unsigned int provides;
+  unsigned int requires;
+  unsigned int obsoletes;
+  unsigned int conflicts;
+  unsigned int recommends;
+  unsigned int supplements;
+  unsigned int enhances;
+  unsigned int suggests;
+  unsigned int freshens;
+};
+
+static int
+rpm2solv(Pool *pool, Source *source, Solvable *s, struct deps *deps, RpmHead *rpmhead)
+{
+  char *name;
+  char *evr;
+
+  name = headstring(rpmhead, TAG_NAME);
+  if (!strcmp(name, "gpg-pubkey"))
+    return 0;
+  s->name = str2id(pool, name, 1);
+  if (!s->name)
+    {
+      fprintf(stderr, "package has no name\n");
+      exit(1);
+    }
+  s->arch = str2id(pool, headstring(rpmhead, TAG_ARCH), 1);
+  if (!s->arch)
+    {
+      fprintf(stderr, "package %s has no arch\n", id2str(pool, s->name));
+      exit(1);
+    }
+  evr = headtoevr(rpmhead);
+  s->evr = str2id(pool, evr, 1);
+  free(evr);
+
+  deps->provides = makedeps(pool, source, rpmhead, TAG_PROVIDENAME, TAG_PROVIDEVERSION, TAG_PROVIDEFLAGS, 0);
+  deps->provides = addfileprovides(pool, source, rpmhead, deps->provides);
+  deps->provides = source_addid_dep(source, deps->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+  deps->requires = makedeps(pool, source, rpmhead, TAG_REQUIRENAME, TAG_REQUIREVERSION, TAG_REQUIREFLAGS, 0);
+  deps->conflicts = makedeps(pool, source, rpmhead, TAG_CONFLICTNAME, TAG_CONFLICTVERSION, TAG_CONFLICTFLAGS, 0);
+  deps->obsoletes = makedeps(pool, source, rpmhead, TAG_OBSOLETENAME, TAG_OBSOLETEVERSION, TAG_OBSOLETEFLAGS, 0);
+
+  deps->recommends = makedeps(pool, source, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, 2);
+  deps->suggests = makedeps(pool, source, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, 1);
+  deps->supplements = makedeps(pool, source, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, 2);
+  deps->enhances  = makedeps(pool, source, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, 1);
+  deps->freshens = 0;
+  return 1;
+}
+
+
+/*
+ * read rpm db as source
+ * 
+ */
+
+Source *
+pool_addsource_rpmdb(Pool *pool, Source *ref)
+{
+  unsigned char buf[16];
+  DB *db = 0;
+  DBC *dbc = 0;
+  int byteswapped;
+  unsigned int dbid;
+  unsigned char *dp, *dbidp;
+  int dl, nrpmids;
+  struct rpmid *rpmids, *rp;
+  int i;
+  int rpmheadsize;
+  RpmHead *rpmhead;
+  Source *source;
+  Solvable *s;
+  struct deps *deps;
+  Id id, *refhash;
+  unsigned int refmask, h;
+  int asolv;
+
+  source = pool_addsource_empty(pool);
+
+  if (ref && !(ref->nsolvables && ref->rpmdbid))
+    ref = 0;
+
+  if (db_create(&db, 0, 0))
+    {
+      perror("db_create");
+      exit(1);
+    }
+
+  if (!ref)
+    {
+      if (db->open(db, 0, "/var/lib/rpm/Packages", 0, DB_HASH, DB_RDONLY, 0664))
+       {
+         perror("db->open /var/lib/rpm/Packages");
+         exit(1);
+       }
+      if (db->get_byteswapped(db, &byteswapped))
+       {
+         perror("db->get_byteswapped");
+         exit(1);
+       }
+      if (db->cursor(db, NULL, &dbc, 0))
+       {
+         perror("db->cursor");
+         exit(1);
+       }
+      dbidp = (unsigned char *)&dbid;
+      pool->solvables = realloc(pool->solvables, (pool->nsolvables + 256) * sizeof(Solvable));
+      memset(pool->solvables + source->start, 0, 256 * sizeof(Solvable));
+      source->rpmdbid = calloc(256, sizeof(unsigned int));
+      deps = calloc(256, sizeof(*deps));
+      asolv = 256;
+      rpmheadsize = 0;
+      rpmhead = 0;
+      i = 0;
+      while (dbc->c_get(dbc, &key, &data, DB_NEXT) == 0)
+       {
+         if (i >= asolv)
+           {
+             pool->solvables = realloc(pool->solvables, (pool->nsolvables + asolv + 256) * sizeof(Solvable));
+             memset(pool->solvables + source->start + asolv, 0, 256 * sizeof(Solvable));
+             source->rpmdbid = realloc(source->rpmdbid, (asolv + 256) * sizeof(unsigned int));
+             memset(source->rpmdbid + asolv, 0, 256 * sizeof(unsigned int));
+             deps = realloc(deps, (asolv + 256) * sizeof(*deps));
+             memset(deps + asolv, 0, 256 * sizeof(*deps));
+             asolv += 256;
+           }
+          if (key.size != 4)
+           {
+             fprintf(stderr, "corrupt Packages database (key size)\n");
+             exit(1);
+           }
+         dp = key.data;
+         if (byteswapped)
+           {
+             dbidp[0] = dp[3];
+             dbidp[1] = dp[2];
+             dbidp[2] = dp[1];
+             dbidp[3] = dp[0];
+           }
+         else
+           memcpy(dbidp, dp, 4);
+         if (dbid == 0)                /* the join key */
+           continue;
+         if (data.size < 8)
+           {
+             fprintf(stderr, "corrupt rpm database (size %u)\n", data.size);
+             exit(1);
+           }
+         if (!rpmhead)
+           rpmhead = malloc(sizeof(*rpmhead) + data.size);
+         else if (data.size > rpmheadsize)
+           rpmhead = realloc(rpmhead, sizeof(*rpmhead) + data.size);
+         memcpy(buf, data.data, 8);
+         rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
+         rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
+         if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > data.size)
+           {
+             fprintf(stderr, "corrupt rpm database (data size)\n");
+             exit(1);
+           }
+         memcpy(rpmhead->data, (unsigned char *)data.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
+         rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
+         source->rpmdbid[i] = dbid;
+         if (rpm2solv(pool, source, pool->solvables + source->start + i, deps + i, rpmhead))
+           i++;
+       }
+      nrpmids = i;
+      dbc->c_close(dbc);
+      db->close(db, 0);
+      db = 0;
+    }
+  else
+    {
+      if (db->open(db, 0, "/var/lib/rpm/Name", 0, DB_HASH, DB_RDONLY, 0664))
+       {
+         perror("db->open /var/lib/rpm/Name");
+         exit(1);
+       }
+      if (db->get_byteswapped(db, &byteswapped))
+       {
+         perror("db->get_byteswapped");
+         exit(1);
+       }
+      if (db->cursor(db, NULL, &dbc, 0))
+       {
+         perror("db->cursor");
+         exit(1);
+       }
+      dbidp = (unsigned char *)&dbid;
+      nrpmids = 0;
+      rpmids = 0;
+      while (dbc->c_get(dbc, &key, &data, DB_NEXT) == 0)
+       {
+         if (key.size == 10 && !memcmp(key.data, "gpg-pubkey", 10))
+           continue;
+         dl = data.size;
+         dp = data.data;
+         while(dl >= 8)
+           {
+             if (byteswapped)
+               {
+                 dbidp[0] = dp[3];
+                 dbidp[1] = dp[2];
+                 dbidp[2] = dp[1];
+                 dbidp[3] = dp[0];
+               }
+             else
+               memcpy(dbidp, dp, 4);
+             if ((nrpmids & 255) == 0)
+               {
+                 if (rpmids)
+                   rpmids = realloc(rpmids, sizeof(*rpmids) * (nrpmids + 256));
+                 else
+                   rpmids = malloc(sizeof(*rpmids) * 256);
+               }
+             rpmids[nrpmids].dbid = dbid;
+             rpmids[nrpmids].name = malloc((int)key.size + 1);
+             memcpy(rpmids[nrpmids].name, key.data, (int)key.size);
+             rpmids[nrpmids].name[(int)key.size] = 0;
+             nrpmids++;
+             dp += 8;
+             dl -= 8;
+           }
+         
+       }
+      dbc->c_close(dbc);
+      db->close(db, 0);
+      db = 0;
+
+      rp = rpmids;
+      dbidp = (unsigned char *)&dbid;
+      rpmheadsize = 0;
+      rpmhead = 0;
+
+      pool->solvables = realloc(pool->solvables, (pool->nsolvables + nrpmids) * sizeof(Solvable));
+      memset(pool->solvables + source->start, 0, nrpmids * sizeof(Solvable));
+      source->rpmdbid = calloc(nrpmids, sizeof(unsigned int));
+      deps = calloc(nrpmids, sizeof(*deps));
+
+      refhash = 0;
+      refmask = 0;
+      if (ref)
+       {
+         refmask = mkmask(ref->nsolvables);
+         refhash = calloc(refmask + 1, sizeof(Id));
+         for (i = 0; i < ref->nsolvables; i++)
+           {
+             h = ref->rpmdbid[i] & refmask;
+             while (refhash[h])
+               h = (h + 317) & refmask;
+             refhash[h] = i + 1;       /* make it non-zero */
+           }
+       }
+      s = pool->solvables + source->start;
+      for (i = 0; i < nrpmids; i++, rp++, s++)
+       {
+         dbid = rp->dbid;
+         source->rpmdbid[i] = dbid;
+         if (refhash)
+           {
+             h = dbid & refmask;
+             while ((id = refhash[h]))
+               {
+                 if (ref->rpmdbid[id - 1] == dbid)
+                   break;
+                 h = (h + 317) & refmask;
+               }
+             if (id)
+               {
+                 Solvable *r = ref->pool->solvables + ref->start + (id - 1);
+                 if (pool == ref->pool)
+                   {
+                     s->name = r->name;
+                     s->evr = r->evr;
+                     s->arch = r->arch;
+                   }
+                 else
+                   {
+                     if (r->name)
+                       s->name = str2id(pool, id2str(ref->pool, r->name), 1);
+                     if (r->evr)
+                       s->evr = str2id(pool, id2str(ref->pool, r->evr), 1);
+                     if (r->arch)
+                       s->arch = str2id(pool, id2str(ref->pool, r->arch), 1);
+                   }
+                 deps[i].provides = copydeps(pool, source, r->provides, ref->pool);
+                 deps[i].requires = copydeps(pool, source, r->requires, ref->pool);
+                 deps[i].conflicts = copydeps(pool, source, r->conflicts, ref->pool);
+                 deps[i].obsoletes = copydeps(pool, source, r->obsoletes, ref->pool);
+                 deps[i].recommends = copydeps(pool, source, r->recommends, ref->pool);
+                 deps[i].suggests = copydeps(pool, source, r->suggests, ref->pool);
+                 deps[i].supplements = copydeps(pool, source, r->supplements, ref->pool);
+                 deps[i].enhances  = copydeps(pool, source, r->enhances, ref->pool);
+                 deps[i].freshens = copydeps(pool, source, r->freshens, ref->pool);
+                 continue;
+               }
+           }
+         if (!db)
+           {
+             if (db_create(&db, 0, 0))
+               {
+                 perror("db_create");
+                 exit(1);
+               }
+             if (db->open(db, 0, "/var/lib/rpm/Packages", 0, DB_HASH, DB_RDONLY, 0664))
+               {
+                 perror("db->open /var/lib/rpm/Packages");
+                 exit(1);
+               }
+             if (db->get_byteswapped(db, &byteswapped))
+               {
+                 perror("db->get_byteswapped");
+                 exit(1);
+               }
+           }
+         if (byteswapped)
+           {
+             buf[0] = dbidp[3];
+             buf[1] = dbidp[2];
+             buf[2] = dbidp[1];
+             buf[3] = dbidp[0];
+           }
+         else
+           memcpy(buf, dbidp, 4);
+         key.data = buf;
+         key.size = 4;
+         data.data = 0;
+         data.size = 0;
+         if (db->get(db, NULL, &key, &data, 0))
+           {
+             perror("db->get");
+             fprintf(stderr, "corrupt rpm database\n");
+             exit(1);
+           }
+         if (data.size < 8)
+           {
+             fprintf(stderr, "corrupt rpm database (size)\n");
+             exit(1);
+           }
+         if (!rpmhead)
+           rpmhead = malloc(sizeof(*rpmhead) + data.size);
+         else if (data.size > rpmheadsize)
+           rpmhead = realloc(rpmhead, sizeof(*rpmhead) + data.size);
+         memcpy(buf, data.data, 8);
+         rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
+         rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
+         if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > data.size)
+           {
+             fprintf(stderr, "corrupt rpm database (data size)\n");
+             exit(1);
+           }
+         memcpy(rpmhead->data, (unsigned char *)data.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
+         rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
+
+         rpm2solv(pool, source, s, deps + i, rpmhead);
+       }
+
+      if (refhash)
+       free(refhash);
+      if (rpmids)
+       {
+         for (i = 0; i < nrpmids; i++)
+           free(rpmids[i].name);
+         free(rpmids);
+       }
+    }
+  if (rpmhead)
+    free(rpmhead);
+  pool->nsolvables += nrpmids;
+  source->nsolvables = nrpmids;
+
+  // copy solvable data to pool
+  s = pool->solvables + source->start;
+  for (i = 0; i < nrpmids; i++, s++)
+    {
+      if (deps[i].provides)
+       s->provides = source->idarraydata + deps[i].provides;
+      if (deps[i].requires)
+       s->requires = source->idarraydata + deps[i].requires;
+      if (deps[i].conflicts)
+       s->conflicts = source->idarraydata + deps[i].conflicts;
+      if (deps[i].obsoletes)
+       s->obsoletes = source->idarraydata + deps[i].obsoletes;
+      if (deps[i].recommends)
+       s->recommends = source->idarraydata + deps[i].recommends;
+      if (deps[i].supplements)
+       s->supplements = source->idarraydata + deps[i].supplements;
+      if (deps[i].suggests)
+       s->suggests = source->idarraydata + deps[i].suggests;
+      if (deps[i].enhances)
+       s->enhances = source->idarraydata + deps[i].enhances;
+      if (deps[i].freshens)
+       s->freshens = source->idarraydata + deps[i].freshens;
+    }
+  free(deps);
+  if (db)
+    db->close(db, 0);
+  return source;
+}
diff --git a/tools/source_rpmdb.h b/tools/source_rpmdb.h
new file mode 100644 (file)
index 0000000..d17d418
--- /dev/null
@@ -0,0 +1 @@
+extern Source *pool_addsource_rpmdb(Pool *pool, Source *ref);
diff --git a/tools/source_rpmmd.c b/tools/source_rpmmd.c
new file mode 100644 (file)
index 0000000..709d567
--- /dev/null
@@ -0,0 +1,503 @@
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <expat.h>
+
+#include "pool.h"
+#include "source_rpmmd.h"
+
+
+enum state {
+  STATE_START,
+  STATE_METADATA,
+  STATE_PACKAGE,
+  STATE_NAME,
+  STATE_ARCH,
+  STATE_VERSION,
+  STATE_FORMAT,
+  STATE_PROVIDES,
+  STATE_PROVIDESENTRY,
+  STATE_REQUIRES,
+  STATE_REQUIRESENTRY,
+  STATE_OBSOLETES,
+  STATE_OBSOLETESENTRY,
+  STATE_CONFLICTS,
+  STATE_CONFLICTSENTRY,
+  STATE_RECOMMENDS,
+  STATE_RECOMMENDSENTRY,
+  STATE_SUPPLEMENTS,
+  STATE_SUPPLEMENTSENTRY,
+  STATE_SUGGESTS,
+  STATE_SUGGESTSENTRY,
+  STATE_ENHANCES,
+  STATE_ENHANCESENTRY,
+  STATE_FRESHENS,
+  STATE_FRESHENSENTRY,
+  STATE_FILE,
+  NUMSTATES
+};
+
+
+struct stateswitch {
+  enum state from;
+  char *ename;
+  enum state to;
+  int docontent;
+};
+
+static struct stateswitch stateswitches[] = {
+  { STATE_START,       "metadata",        STATE_METADATA, 0 },
+  { STATE_METADATA,    "package",         STATE_PACKAGE, 0 },
+  { STATE_PACKAGE,     "name",            STATE_NAME, 1 },
+  { STATE_PACKAGE,     "arch",            STATE_ARCH, 1 },
+  { STATE_PACKAGE,     "version",         STATE_VERSION, 0 },
+  { STATE_PACKAGE,     "format",          STATE_FORMAT, 0 },
+  { STATE_FORMAT,      "rpm:provides",    STATE_PROVIDES, 0 },
+  { STATE_FORMAT,      "rpm:requires",    STATE_REQUIRES, 0 },
+  { STATE_FORMAT,      "rpm:obsoletes",   STATE_OBSOLETES , 0 },
+  { STATE_FORMAT,      "rpm:conflicts",   STATE_CONFLICTS , 0 },
+  { STATE_FORMAT,      "rpm:recommends" , STATE_RECOMMENDS , 0 },
+  { STATE_FORMAT,      "rpm:supplements", STATE_SUPPLEMENTS, 0 },
+  { STATE_FORMAT,      "rpm:suggests",    STATE_SUGGESTS, 0 },
+  { STATE_FORMAT,      "rpm:enhances",    STATE_ENHANCES, 0 },
+  { STATE_FORMAT,      "rpm:freshens",    STATE_FRESHENS, 0 },
+  { STATE_FORMAT,      "file",            STATE_FILE, 1 },
+  { STATE_PROVIDES,    "rpm:entry",       STATE_PROVIDESENTRY, 0 },
+  { STATE_REQUIRES,    "rpm:entry",       STATE_REQUIRESENTRY, 0 },
+  { STATE_OBSOLETES,   "rpm:entry",       STATE_OBSOLETESENTRY, 0 },
+  { STATE_CONFLICTS,   "rpm:entry",       STATE_CONFLICTSENTRY, 0 },
+  { STATE_RECOMMENDS,  "rpm:entry",       STATE_RECOMMENDSENTRY, 0 },
+  { STATE_SUPPLEMENTS, "rpm:entry",       STATE_SUPPLEMENTSENTRY, 0 },
+  { STATE_SUGGESTS,    "rpm:entry",       STATE_SUGGESTSENTRY, 0 },
+  { STATE_ENHANCES,    "rpm:entry",       STATE_ENHANCESENTRY, 0 },
+  { STATE_FRESHENS,    "rpm:entry",       STATE_FRESHENSENTRY, 0 },
+  { NUMSTATES}
+};
+
+struct deps {
+  unsigned int provides;
+  unsigned int requires;
+  unsigned int obsoletes;
+  unsigned int conflicts;
+  unsigned int recommends;
+  unsigned int supplements;
+  unsigned int enhances;
+  unsigned int suggests;
+  unsigned int freshens;
+};
+
+struct parsedata {
+  int depth;
+  enum state state;
+  int statedepth;
+  char *content;
+  int lcontent;
+  int acontent;
+  int docontent;
+  int numpacks;
+  int pack;
+  Pool *pool;
+  Source *source;
+  Solvable *start;
+  struct deps *deps;
+  struct stateswitch *swtab[NUMSTATES];
+  enum state sbtab[NUMSTATES];
+};
+
+static Id
+makeevr_atts(Pool *pool, struct parsedata *pd, const char **atts)
+{
+  const char *e, *v, *r, *v2;
+  char *c;
+  int l;
+
+  e = v = r = 0;
+  for (; *atts; atts += 2)
+    {
+      if (!strcmp(*atts, "epoch"))
+       e = atts[1];
+      else if (!strcmp(*atts, "ver"))
+       v = atts[1];
+      else if (!strcmp(*atts, "rel"))
+       r = atts[1];
+    }
+  if (e && !strcmp(e, "0"))
+    e = 0;
+  if (v && !e)
+    {
+      for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)
+        ;
+      if (v2 > v && *v2 == ':')
+       e = "0";
+    }
+  l = 1;
+  if (e)
+    l += strlen(e) + 1;
+  if (v)
+    l += strlen(v);
+  if (r)
+    l += strlen(r) + 1;
+  if (l > pd->acontent)
+    {
+      pd->content = realloc(pd->content, l + 256);
+      pd->acontent = l + 256;
+    }
+  c = pd->content;
+  if (e)
+    {
+      strcpy(c, e);
+      c += strlen(c);
+      *c++ = ':';
+    }
+  if (v)
+    {
+      strcpy(c, v);
+      c += strlen(c);
+    }
+  if (r)
+    {
+      *c++ = '-';
+      strcpy(c, r);
+      c += strlen(c);
+    }
+  *c = 0;
+  if (!*pd->content)
+    return 0;
+#if 0
+  fprintf(stderr, "evr: %s\n", pd->content);
+#endif
+  return str2id(pool, pd->content, 1);
+}
+
+static char *flagtab[] = {
+  "GT",
+  "EQ",
+  "GE",
+  "LT",
+  "NE",
+  "LE"
+};
+
+static unsigned int
+adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts, int isreq)
+{
+  Id id, name;
+  const char *n, *f, *k;
+  const char **a;
+
+  n = f = k = 0;
+  for (a = atts; *a; a += 2)
+    {
+      if (!strcmp(*a, "name"))
+       n = a[1];
+      else if (!strcmp(*a, "flags"))
+       f = a[1];
+      else if (!strcmp(*a, "kind"))
+       k = a[1];
+      else if (isreq && !strcmp(*a, "pre") && a[1][0] == '1')
+       isreq = 2;
+    }
+  if (!n)
+    return olddeps;
+  if (k && !strcmp(k, "package"))
+    k = 0;
+  if (k)
+    {
+      int l = strlen(k) + 1 + strlen(n) + 1;
+      if (l > pd->acontent)
+       {
+         pd->content = realloc(pd->content, l + 256);
+         pd->acontent = l + 256;
+       }
+      sprintf(pd->content, "%s:%s", k, n); 
+      name = str2id(pool, pd->content, 1); 
+    }
+  else
+    name = str2id(pool, (char *)n, 1);
+  if (f)
+    {
+      Id evr = makeevr_atts(pool, pd, atts);
+      int flags;
+      for (flags = 0; flags < 6; flags++)
+       if (!strcmp(f, flagtab[flags]))
+         break;
+      flags = flags < 6 ? flags + 1 : 0;
+      id = rel2id(pool, name, evr, flags, 1);
+    }
+  else
+    id = name;
+#if 0
+  fprintf(stderr, "new dep %s%s%s\n", id2str(pool, d), id2rel(pool, d), id2evr(pool, d));
+#endif
+  return source_addid_dep(pd->source, olddeps, id, isreq);
+}
+
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+  struct parsedata *pd = userData;
+  Pool *pool = pd->pool;
+  Solvable *s = pd->start ? pd->start + pd->pack : 0;
+  struct stateswitch *sw;
+
+  if (pd->depth != pd->statedepth)
+    {
+      pd->depth++;
+      return;
+    }
+  pd->depth++;
+  for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
+    if (!strcmp(sw->ename, name))
+      break;
+  if (sw->from != pd->state)
+    {
+#if 0
+      fprintf(stderr, "into unknown: %s\n", name);
+#endif
+      return;
+    }
+  pd->state = sw->to;
+  pd->docontent = sw->docontent;
+  pd->statedepth = pd->depth;
+  pd->lcontent = 0;
+  *pd->content = 0;
+  switch(pd->state)
+    {
+    case STATE_METADATA:
+      for (; *atts; atts += 2)
+       {
+         if (!strcmp(*atts, "packages"))
+           {
+             pd->numpacks = atoi(atts[1]);
+#if 0
+             fprintf(stderr, "numpacks: %d\n", pd->numpacks);
+#endif
+             pool->solvables = realloc(pool->solvables, (pool->nsolvables + pd->numpacks) * sizeof(Solvable));
+             pd->start = pool->solvables + pd->source->start;
+             memset(pd->start, 0, pd->numpacks * sizeof(Solvable));
+             pd->deps = calloc(pd->numpacks, sizeof(struct deps));
+           }
+       }
+      break;
+    case STATE_PACKAGE:
+      if (pd->pack >= pd->numpacks)
+       {
+         fprintf(stderr, "repomd lied about the package number\n");
+         exit(1);
+       }
+#if 0
+      fprintf(stderr, "package #%d\n", pd->pack);
+#endif
+      break;
+    case STATE_VERSION:
+      s->evr = makeevr_atts(pool, pd, atts);
+      break;
+    case STATE_PROVIDES:
+      pd->deps[pd->pack].provides = 0;
+      break;
+    case STATE_PROVIDESENTRY:
+      pd->deps[pd->pack].provides = adddep(pool, pd, pd->deps[pd->pack].provides, atts, 0);
+      break;
+    case STATE_REQUIRES:
+      pd->deps[pd->pack].requires = 0;
+      break;
+    case STATE_REQUIRESENTRY:
+      pd->deps[pd->pack].requires = adddep(pool, pd, pd->deps[pd->pack].requires, atts, 1);
+      break;
+    case STATE_OBSOLETES:
+      pd->deps[pd->pack].obsoletes = 0;
+      break;
+    case STATE_OBSOLETESENTRY:
+      pd->deps[pd->pack].obsoletes = adddep(pool, pd, pd->deps[pd->pack].obsoletes, atts, 0);
+      break;
+    case STATE_CONFLICTS:
+      pd->deps[pd->pack].conflicts = 0;
+      break;
+    case STATE_CONFLICTSENTRY:
+      pd->deps[pd->pack].conflicts = adddep(pool, pd, pd->deps[pd->pack].conflicts, atts, 0);
+      break;
+    case STATE_RECOMMENDS:
+      pd->deps[pd->pack].recommends = 0;
+      break;
+    case STATE_RECOMMENDSENTRY:
+      pd->deps[pd->pack].recommends = adddep(pool, pd, pd->deps[pd->pack].recommends, atts, 0);
+      break;
+    case STATE_SUPPLEMENTS:
+      pd->deps[pd->pack].supplements= 0;
+      break;
+    case STATE_SUPPLEMENTSENTRY:
+      pd->deps[pd->pack].supplements = adddep(pool, pd, pd->deps[pd->pack].supplements, atts, 0);
+      break;
+    case STATE_SUGGESTS:
+      pd->deps[pd->pack].suggests = 0;
+      break;
+    case STATE_SUGGESTSENTRY:
+      pd->deps[pd->pack].suggests = adddep(pool, pd, pd->deps[pd->pack].suggests, atts, 0);
+      break;
+    case STATE_ENHANCES:
+      pd->deps[pd->pack].enhances = 0;
+      break;
+    case STATE_ENHANCESENTRY:
+      pd->deps[pd->pack].enhances = adddep(pool, pd, pd->deps[pd->pack].enhances, atts, 0);
+      break;
+    case STATE_FRESHENS:
+      pd->deps[pd->pack].freshens = 0;
+      break;
+    case STATE_FRESHENSENTRY:
+      pd->deps[pd->pack].freshens = adddep(pool, pd, pd->deps[pd->pack].freshens, atts, 0);
+      break;
+    default:
+      break;
+    }
+}
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+  struct parsedata *pd = userData;
+  Pool *pool = pd->pool;
+  Solvable *s = pd->start ? pd->start + pd->pack : 0;
+  Id id;
+
+  if (pd->depth != pd->statedepth)
+    {
+      pd->depth--;
+      // printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+      return;
+    }
+  pd->depth--;
+  pd->statedepth--;
+  switch (pd->state)
+    {
+    case STATE_PACKAGE:
+#if 0
+       {
+         const char *arch = id2str(pool, s->arch);
+         if (strcmp(arch, "noarch") && strcmp(arch, "i586") && strcmp(arch, "i686"))
+           break;
+       }
+#endif
+      if (!s->arch)
+        s->arch = ARCH_NOARCH;
+      if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+        pd->deps[pd->pack].provides = source_addid_dep(pd->source, pd->deps[pd->pack].provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+      pd->pack++;
+      break;
+    case STATE_NAME:
+      s->name = str2id(pool, pd->content, 1);
+      break;
+    case STATE_ARCH:
+      s->arch = str2id(pool, pd->content, 1);
+      break;
+    case STATE_FILE:
+      id = str2id(pool, pd->content, 1);
+      pd->deps[pd->pack].provides = source_addid(pd->source, pd->deps[pd->pack].provides, id);
+      break;
+    default:
+      break;
+    }
+  pd->state = pd->sbtab[pd->state];
+  pd->docontent = 0;
+  // printf("back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+}
+
+static void XMLCALL
+characterData(void *userData, const XML_Char *s, int len)
+{
+  struct parsedata *pd = userData;
+  int l;
+  char *c;
+
+  if (!pd->docontent)
+    return;
+  l = pd->lcontent + len + 1;
+  if (l > pd->acontent)
+    {
+      pd->content = realloc(pd->content, l + 256);
+      pd->acontent = l + 256;
+    }
+  c = pd->content + pd->lcontent;
+  pd->lcontent += len;
+  while (len-- > 0)
+    *c++ = *s++;
+  *c = 0;
+}
+
+
+#define BUFF_SIZE 8192
+
+Source *
+pool_addsource_rpmmd(Pool *pool, FILE *fp)
+{
+  struct parsedata pd;
+  char buf[BUFF_SIZE];
+  int i, l;
+  Source *source;
+  Solvable *s;
+  struct deps *deps;
+  struct stateswitch *sw;
+
+  source = pool_addsource_empty(pool);
+  memset(&pd, 0, sizeof(pd));
+  for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
+    {
+      if (!pd.swtab[sw->from])
+        pd.swtab[sw->from] = sw;
+      pd.sbtab[sw->to] = sw->from;
+    }
+  pd.pool = pool;
+  pd.source = source;
+  pd.content = malloc(256);
+  pd.acontent = 256;
+  pd.lcontent = 0;
+  XML_Parser parser = XML_ParserCreate(NULL);
+  XML_SetUserData(parser, &pd);
+  XML_SetElementHandler(parser, startElement, endElement);
+  XML_SetCharacterDataHandler(parser, characterData);
+  for (;;)
+    {
+      l = fread(buf, 1, sizeof(buf), fp);
+      if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
+       {
+         fprintf(stderr, "%s at line %u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
+         exit(1);
+       }
+      if (l == 0)
+       break;
+    }
+  XML_ParserFree(parser);
+
+  pool->nsolvables += pd.pack;
+  source->nsolvables = pd.pack;
+
+  deps = pd.deps;
+  s = pool->solvables + source->start;
+  for (i = 0; i < pd.pack; i++, s++)
+    {
+      if (deps[i].provides)
+        s->provides = source->idarraydata + deps[i].provides;
+      if (deps[i].requires)
+        s->requires = source->idarraydata + deps[i].requires;
+      if (deps[i].conflicts)
+        s->conflicts = source->idarraydata + deps[i].conflicts;
+      if (deps[i].obsoletes)
+        s->obsoletes = source->idarraydata + deps[i].obsoletes;
+      if (deps[i].recommends)
+        s->recommends = source->idarraydata + deps[i].recommends;
+      if (deps[i].supplements)
+        s->supplements = source->idarraydata + deps[i].supplements;
+      if (deps[i].suggests)
+        s->suggests = source->idarraydata + deps[i].suggests;
+      if (deps[i].enhances)
+        s->enhances = source->idarraydata + deps[i].enhances;
+      if (deps[i].freshens)
+        s->freshens = source->idarraydata + deps[i].freshens;
+    }
+  free(deps);
+  free(pd.content);
+  return source;
+}
diff --git a/tools/source_rpmmd.h b/tools/source_rpmmd.h
new file mode 100644 (file)
index 0000000..38cc00d
--- /dev/null
@@ -0,0 +1 @@
+extern Source *pool_addsource_rpmmd(Pool *pool, FILE *fp);
diff --git a/tools/source_susetags.c b/tools/source_susetags.c
new file mode 100644 (file)
index 0000000..b4847e9
--- /dev/null
@@ -0,0 +1,352 @@
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "source_susetags.h"
+
+#define PACK_BLOCK 255
+
+static int
+split(char *l, char **sp, int m)
+{
+  int i;
+  for (i = 0; i < m;)
+    {
+      while (*l == ' ')
+       l++;
+      if (!*l)
+       break;
+      sp[i++] = l;
+      while (*l && *l != ' ')
+       l++;
+      if (!*l)
+       break;
+      *l++ = 0;
+    }
+  return i;
+}
+
+struct deps {
+  unsigned int provides;
+  unsigned int requires;
+  unsigned int obsoletes;
+  unsigned int conflicts;
+  unsigned int recommends;
+  unsigned int supplements;
+  unsigned int enhances;
+  unsigned int suggests;
+  unsigned int freshens;
+};
+
+struct parsedata {
+  char *kind;
+  Source *source;
+  char *tmp;
+  int tmpl;
+};
+
+static Id
+makeevr(Pool *pool, char *s)
+{
+  if (!strncmp(s, "0:", 2) && s[2])
+    s += 2;
+  return str2id(pool, s, 1);
+}
+
+static char *flagtab[] = {
+  ">",
+  "=",
+  ">=",
+  "<",
+  "!=",
+  "<="
+};
+
+static char *
+join(struct parsedata *pd, char *s1, char *s2, char *s3)
+{
+  int l = 1;
+  char *p;
+
+  if (s1)
+    l += strlen(s1);
+  if (s2)
+    l += strlen(s2);
+  if (s3)
+    l += strlen(s3);
+  if (l > pd->tmpl)
+    {
+      pd->tmpl = l + 256;
+      if (!pd->tmp)
+       pd->tmp = malloc(pd->tmpl);
+      else
+       pd->tmp = realloc(pd->tmp, pd->tmpl);
+    }
+  p = pd->tmp;
+  if (s1)
+    {
+      strcpy(p, s1);
+      p += strlen(s1);
+    }
+  if (s2)
+    {
+      strcpy(p, s2);
+      p += strlen(s2);
+    }
+  if (s3)
+    {
+      strcpy(p, s3);
+      p += strlen(s3);
+    }
+  return pd->tmp;
+}
+
+static unsigned int
+adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, int isreq, char *kind)
+{
+  int i, flags;
+  Id id, evrid;
+  char *sp[4];
+
+  i = split(line + 5, sp, 4);
+  if (i != 1 && i != 3)
+    {
+      fprintf(stderr, "Bad dependency line: %s\n", line);
+      exit(1);
+    }
+  if (kind)
+    id = str2id(pool, join(pd, kind, ":", sp[0]), 1);
+  else
+    id = str2id(pool, sp[0], 1);
+  if (i == 3)
+    {
+      evrid = makeevr(pool, sp[2]);
+      for (flags = 0; flags < 6; flags++)
+        if (!strcmp(sp[1], flagtab[flags]))
+          break;
+      if (flags == 6)
+       {
+         fprintf(stderr, "Unknown relation '%s'\n", sp[1]);
+         exit(1);
+       }
+      id = rel2id(pool, id, evrid, flags + 1, 1);
+    }
+  return source_addid_dep(pd->source, olddeps, id, isreq);
+}
+
+Source *
+pool_addsource_susetags(Pool *pool, FILE *fp)
+{
+  char *line, *linep;
+  int aline;
+  Source *source;
+  Solvable *s;
+  struct deps *deps = 0, *dp = 0;
+  int intag = 0;
+  int cummulate = 0;
+  int pack;
+  char *sp[5];
+  int i;
+  struct parsedata pd;
+
+  source = pool_addsource_empty(pool);
+  memset(&pd, 0, sizeof(pd));
+  line = malloc(1024);
+  aline = 1024;
+
+  pd.source = source;
+
+  linep = line;
+  pack = 0;
+  s = 0;
+
+  for (;;)
+    {
+      if (linep - line + 16 > aline)
+       {
+         aline = linep - line;
+         line = realloc(line, aline + 512);
+         linep = line + aline;
+         aline += 512;
+       }
+      if (!fgets(linep, aline - (linep - line), fp))
+       break;
+      linep += strlen(linep);
+      if (linep == line || linep[-1] != '\n')
+        continue;
+      *--linep = 0;
+      if (intag)
+       {
+         int isend = linep[-intag - 2] == '-' && linep[-1] == ':' && !strncmp(linep - 1 - intag, line + 1, intag) && (linep == line + 1 + intag + 1 + 1 + 1 + intag + 1 || linep[-intag - 3] == '\n');
+         if (cummulate && !isend)
+           {
+             *linep++ = '\n';
+             continue;
+           }
+         if (cummulate && isend)
+           {
+             linep[-intag - 2] = 0;
+             linep = line;
+             intag = 0;
+           }
+         if (!cummulate && isend)
+           {
+             intag = 0;
+             linep = line;
+             continue;
+           }
+         if (!cummulate && !isend)
+           linep = line + intag + 3;
+       }
+      else
+       linep = line;
+      if (!intag && line[0] == '+' && line[1] && line[1] != ':')
+       {
+         char *tagend = strchr(line, ':');
+         if (!tagend)
+           {
+             fprintf(stderr, "bad line: %s\n", line);
+             exit(1);
+           }
+         intag = tagend - (line + 1);
+         line[0] = '=';
+         line[intag + 2] = ' ';
+         linep = line + intag + 3;
+         continue;
+       }
+      if (*line == '#' || !*line)
+       continue;
+      if (!strncmp(line, "=Pkg:", 5) || !strncmp(line, "=Pat:", 5))
+       {
+         if (dp)
+           dp->provides = source_addid_dep(source, dp->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+         pd.kind = 0;
+         if (line[3] == 't')
+           pd.kind = "pattern";
+         if ((pack & PACK_BLOCK) == 0)
+           {
+             pool->solvables = realloc(pool->solvables, (pool->nsolvables + pack + PACK_BLOCK + 1) * sizeof(Solvable));
+             memset(pool->solvables + source->start + pack, 0, (PACK_BLOCK + 1) * sizeof(Solvable));
+             if (!deps)
+               deps = malloc((pack + PACK_BLOCK + 1) * sizeof(struct deps));
+             else
+               deps = realloc(deps, (pack + PACK_BLOCK + 1) * sizeof(struct deps));
+             memset(deps + pack, 0, (PACK_BLOCK + 1) * sizeof(struct deps));
+           }
+         s = pool->solvables + source->start + pack;
+         dp = deps + pack;
+         pack++;
+          if (split(line + 5, sp, 5) != 4)
+           {
+             fprintf(stderr, "Bad line: %s\n", line);
+             exit(1);
+           }
+         if (pd.kind)
+           s->name = str2id(pool, join(&pd, pd.kind, ":", sp[0]), 1);
+         else
+           s->name = str2id(pool, sp[0], 1);
+         s->evr = makeevr(pool, join(&pd, sp[1], "-", sp[2]));
+         s->arch = str2id(pool, sp[3], 1);
+         continue;
+       }
+      if (!strncmp(line, "=Prv:", 5))
+       {
+         dp->provides = adddep(pool, &pd, dp->provides, line, 0, pd.kind);
+         continue;
+       }
+      if (!strncmp(line, "=Req:", 5))
+       {
+         dp->requires = adddep(pool, &pd, dp->requires, line, 1, pd.kind);
+         continue;
+       }
+      if (!strncmp(line, "=Prq:", 5))
+       {
+         if (pd.kind)
+           dp->requires = adddep(pool, &pd, dp->requires, line, 0, 0);
+         else
+           dp->requires = adddep(pool, &pd, dp->requires, line, 2, 0);
+         continue;
+       }
+      if (!strncmp(line, "=Obs:", 5))
+       {
+         dp->obsoletes = adddep(pool, &pd, dp->obsoletes, line, 0, pd.kind);
+         continue;
+       }
+      if (!strncmp(line, "=Con:", 5))
+       {
+         dp->conflicts = adddep(pool, &pd, dp->conflicts, line, 0, pd.kind);
+         continue;
+       }
+      if (!strncmp(line, "=Rec:", 5))
+       {
+         dp->recommends = adddep(pool, &pd, dp->recommends, line, 0, pd.kind);
+         continue;
+       }
+      if (!strncmp(line, "=Sup:", 5))
+       {
+         dp->supplements = adddep(pool, &pd, dp->supplements, line, 0, pd.kind);
+         continue;
+       }
+      if (!strncmp(line, "=Enh:", 5))
+       {
+         dp->enhances = adddep(pool, &pd, dp->enhances, line, 0, pd.kind);
+         continue;
+       }
+      if (!strncmp(line, "=Sug:", 5))
+       {
+         dp->suggests = adddep(pool, &pd, dp->suggests, line, 0, pd.kind);
+         continue;
+       }
+      if (!strncmp(line, "=Fre:", 5))
+       {
+         dp->freshens = adddep(pool, &pd, dp->freshens, line, 0, pd.kind);
+         continue;
+       }
+      if (!strncmp(line, "=Prc:", 5))
+       {
+         dp->recommends = adddep(pool, &pd, dp->recommends, line, 0, 0);
+         continue;
+       }
+      if (!strncmp(line, "=Psg:", 5))
+       {
+         dp->suggests = adddep(pool, &pd, dp->suggests, line, 0, 0);
+         continue;
+       }
+    }
+  if (dp && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+    dp->provides = source_addid_dep(source, dp->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+    
+  pool->nsolvables += pack;
+  source->nsolvables = pack;
+  s = pool->solvables + source->start;
+  for (i = 0; i < pack; i++, s++)
+    {
+      if (deps[i].provides)
+        s->provides = source->idarraydata + deps[i].provides;
+      if (deps[i].requires)
+        s->requires = source->idarraydata + deps[i].requires;
+      if (deps[i].conflicts)
+        s->conflicts = source->idarraydata + deps[i].conflicts;
+      if (deps[i].obsoletes)
+        s->obsoletes = source->idarraydata + deps[i].obsoletes;
+      if (deps[i].recommends)
+        s->recommends = source->idarraydata + deps[i].recommends;
+      if (deps[i].supplements)
+        s->supplements = source->idarraydata + deps[i].supplements;
+      if (deps[i].suggests)
+        s->suggests = source->idarraydata + deps[i].suggests;
+      if (deps[i].enhances)
+        s->enhances = source->idarraydata + deps[i].enhances;
+      if (deps[i].freshens)
+        s->freshens = source->idarraydata + deps[i].freshens;
+    }
+  free(deps);
+  if (pd.tmp)
+    free(pd.tmp);
+  free(line);
+  return source;
+}
diff --git a/tools/source_susetags.h b/tools/source_susetags.h
new file mode 100644 (file)
index 0000000..3938e7c
--- /dev/null
@@ -0,0 +1 @@
+extern Source *pool_addsource_susetags(Pool *pool, FILE *fp);
diff --git a/tools/source_write.c b/tools/source_write.c
new file mode 100644 (file)
index 0000000..2cac212
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * source_write.c
+ * 
+ * Write Source data out to binary file
+ * 
+ * See doc/README.format for a description 
+ * of the binary file format
+ * 
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "source_write.h"
+
+/*------------------------------------------------------------------*/
+/* Id map optimizations */
+
+typedef struct needid {
+  Id need;
+  Id map;
+} NeedId;
+
+/*
+ * increment need Id
+ * idarray: array of Ids, ID_NULL terminated
+ * needid: array of Id->NeedId
+ * 
+ * return count
+ * 
+ */
+
+static int
+incneedid(Pool *pool, Id *idarray, NeedId *needid)
+{
+  if (!idarray)
+    return 0;
+
+  Id id;
+  int n = 0;
+
+  while ((id = *idarray++) != ID_NULL)
+    {
+      n++;
+      while (ISRELDEP(id))
+       {
+         Reldep *rd = GETRELDEP(pool, id);
+         needid[GETRELID(pool, id)].need++;
+         needid[rd->evr].need++;
+         id = rd->name;
+       }
+      needid[id].need++;
+    }
+  return n + 1;
+}
+
+
+/*
+ * 
+ */
+
+static int
+needid_cmp_need(const void *ap, const void *bp)
+{
+  const NeedId *a = ap;
+  const NeedId *b = bp;
+  int r;
+  r = b->need - a->need;
+  if (r)
+    return r;
+  return a->map - b->map;
+}
+
+
+/*------------------------------------------------------------------*/
+/* output routines */
+
+/*
+ * unsigned 32-bit
+ */
+
+static void
+write_u32(FILE *fp, unsigned int x)
+{
+  if (putc(x >> 24, fp) == EOF ||
+      putc(x >> 16, fp) == EOF ||
+      putc(x >> 8, fp) == EOF ||
+      putc(x, fp) == EOF)
+    {
+      perror("write error");
+      exit(1);
+    }
+}
+
+
+/*
+ * unsigned 8-bit
+ */
+
+static void
+write_u8(FILE *fp, unsigned int x)
+{
+  if (putc(x, fp) == EOF)
+    {
+      perror("write error");
+      exit(1);
+    }
+}
+
+
+/*
+ * Id
+ */
+
+static void
+write_id(FILE *fp, Id x)
+{
+  if (x >= (1 << 14))
+    {
+      if (x >= (1 << 28))
+       putc((x >> 28) | 128, fp);
+      if (x >= (1 << 21))
+       putc((x >> 21) | 128, fp);
+      putc((x >> 14) | 128, fp);
+    }
+  if (x >= (1 << 7))
+    putc((x >> 7) | 128, fp);
+  if (putc(x & 127, fp) == EOF)
+    {
+      perror("write error");
+      exit(1);
+    }
+}
+
+
+/*
+ * Array of Ids
+ */
+
+static void
+write_idarray(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
+{
+  Id id;
+  if (!ids)
+    return;
+  if (!*ids)
+    {
+      write_u8(fp, ID_NULL);
+      return;
+    }
+  for (;;)
+    {
+      id = *ids++;
+      id = needid[ISRELDEP(id) ? GETRELID(pool, id) : id].need;
+      if (id >= 64)
+       id = (id & 63) | ((id & ~63) << 1);
+      if (!*ids)
+       {
+         write_id(fp, id);
+         return;
+       }
+      write_id(fp, id | 64);
+    }
+}
+
+
+/*
+ * Source
+ */
+
+void
+pool_writesource(Pool *pool, Source *source, FILE *fp)
+{
+  int i, numsolvdata;
+  Solvable *s, *sstart;
+  NeedId *needid;
+  int nstrings, nrels;
+  unsigned int sizeid;
+  Reldep *ran;
+
+  int idsizes[RPM_RPMDBID + 1];
+  int bits, bitmaps;
+  int nsolvables;
+
+  nsolvables = source->nsolvables;
+  sstart = pool->solvables + source->start;
+
+  needid = (NeedId *)calloc(pool->nstrings + pool->nrels, sizeof(*needid));
+
+  memset(idsizes, 0, sizeof(idsizes));
+
+  for (i = 0; i < nsolvables; i++)
+    {
+      s = sstart + i;
+      needid[s->name].need++;
+      needid[s->arch].need++;
+      needid[s->evr].need++;
+      idsizes[SOLVABLE_PROVIDES]    += incneedid(pool, s->provides, needid);
+      idsizes[SOLVABLE_REQUIRES]    += incneedid(pool, s->requires, needid);
+      idsizes[SOLVABLE_CONFLICTS]   += incneedid(pool, s->conflicts, needid);
+      idsizes[SOLVABLE_OBSOLETES]   += incneedid(pool, s->obsoletes, needid);
+      idsizes[SOLVABLE_RECOMMENDS]  += incneedid(pool, s->recommends, needid);
+      idsizes[SOLVABLE_SUGGESTS]    += incneedid(pool, s->suggests, needid);
+      idsizes[SOLVABLE_SUPPLEMENTS] += incneedid(pool, s->supplements, needid);
+      idsizes[SOLVABLE_ENHANCES]    += incneedid(pool, s->enhances, needid);
+      idsizes[SOLVABLE_FRESHENS]    += incneedid(pool, s->freshens, needid);
+    }
+
+  idsizes[SOLVABLE_NAME] = 1;
+  idsizes[SOLVABLE_ARCH] = 1;
+  idsizes[SOLVABLE_EVR] = 1;
+  if (source->rpmdbid)
+    idsizes[RPM_RPMDBID] = 1;
+
+  for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
+    {
+      if (idsizes[i])
+        needid[i].need++;
+    }
+
+  needid[0].need = 0;
+  needid[pool->nstrings].need = 0;
+  for (i = 0; i < pool->nstrings + pool->nrels; i++)
+    {
+      needid[i].map = i;
+    }
+
+  qsort(needid + 1, pool->nstrings - 1, sizeof(*needid), needid_cmp_need);
+  qsort(needid + pool->nstrings, pool->nrels, sizeof(*needid), needid_cmp_need);
+
+  sizeid = 0;
+  for (i = 1; i < pool->nstrings; i++)
+    {
+      if (!needid[i].need)
+        break;
+      needid[i].need = 0;
+      sizeid += strlen(pool->stringspace + pool->strings[needid[i].map]) + 1;
+    }
+
+  nstrings = i;
+  for (i = 0; i < nstrings; i++)
+    {
+      needid[needid[i].map].need = i;
+    }
+
+  for (i = 0; i < pool->nrels; i++)
+    {
+      if (!needid[pool->nstrings + i].need)
+        break;
+      else
+        needid[pool->nstrings + i].need = 0;
+    }
+
+  nrels = i;
+  for (i = 0; i < nrels; i++)
+    {
+      needid[needid[pool->nstrings + i].map].need = nstrings + i;
+    }
+
+  /* write file header */
+  write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
+  write_u32(fp, SOLV_VERSION);
+
+  /* write counts */
+  write_u32(fp, nstrings);
+  write_u32(fp, nrels);
+  write_u32(fp, nsolvables);
+  write_u32(fp, sizeid);
+
+  /*
+   * write strings
+   */
+  for (i = 1; i < nstrings; i++)
+    {
+      char *str = pool->stringspace + pool->strings[needid[i].map];
+      if (fwrite(str, strlen(str) + 1, 1, fp) != 1)
+       {
+         perror("write error");
+         exit(1);
+       }
+    }
+
+  /*
+   * write RelDeps
+   */
+  for (i = 0; i < nrels; i++)
+    {
+      ran = pool->rels + (needid[pool->nstrings + i].map - pool->nstrings);
+      write_id(fp, needid[ISRELDEP(ran->name) ? GETRELID(pool, ran->name) : ran->name].need);
+      write_id(fp, needid[ISRELDEP(ran->evr) ? GETRELID(pool, ran->evr) : ran->evr].need);
+      write_u8( fp, ran->flags);
+    }
+
+  write_u32(fp, 0);    /* no source data */
+
+  /*
+   * write Solvables
+   */
+  
+  numsolvdata = 0;
+  for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
+    {
+      if (idsizes[i])
+        numsolvdata++;
+    }
+  write_u32(fp, (unsigned int)numsolvdata);
+
+  bitmaps = 0;
+  for (i = SOLVABLE_NAME; i <= SOLVABLE_FRESHENS; i++)
+    {
+      if (!idsizes[i])
+       continue;
+      if (i >= SOLVABLE_PROVIDES && i <= SOLVABLE_FRESHENS)
+       {
+         write_u8(fp, TYPE_IDARRAY|TYPE_BITMAP);
+         bitmaps++;
+       }
+      else
+       write_u8(fp, TYPE_ID);
+      write_id(fp, needid[i].need);
+      if (i >= SOLVABLE_PROVIDES && i <= SOLVABLE_FRESHENS)
+       write_u32(fp, idsizes[i]);
+      else
+       write_u32(fp, 0);
+    }
+
+  if (source->rpmdbid)
+    {
+      write_u8(fp, TYPE_U32);
+      write_id(fp, needid[RPM_RPMDBID].need);
+      write_u32(fp, 0);
+    }
+
+  for (i = 0; i < nsolvables; ++i)
+    {
+      s = sstart + i;
+      bits = 0;
+      if (idsizes[SOLVABLE_FRESHENS])
+       bits = (bits << 1) | (s->freshens ? 1 : 0);
+      if (idsizes[SOLVABLE_ENHANCES])
+       bits = (bits << 1) | (s->enhances ? 1 : 0);
+      if (idsizes[SOLVABLE_SUPPLEMENTS])
+       bits = (bits << 1) | (s->supplements ? 1 : 0);
+      if (idsizes[SOLVABLE_SUGGESTS])
+       bits = (bits << 1) | (s->suggests ? 1 : 0);
+      if (idsizes[SOLVABLE_RECOMMENDS])
+       bits = (bits << 1) | (s->recommends ? 1 : 0);
+      if (idsizes[SOLVABLE_REQUIRES])
+       bits = (bits << 1) | (s->requires ? 1 : 0);
+      if (idsizes[SOLVABLE_CONFLICTS])
+       bits = (bits << 1) | (s->conflicts ? 1 : 0);
+      if (idsizes[SOLVABLE_OBSOLETES])
+       bits = (bits << 1) | (s->obsoletes ? 1 : 0);
+      if (idsizes[SOLVABLE_PROVIDES])
+       bits = (bits << 1) | (s->provides ? 1 : 0);
+
+      if (bitmaps > 24)
+       write_u8(fp, bits >> 24);
+      if (bitmaps > 16)
+       write_u8(fp, bits >> 16);
+      if (bitmaps > 8)
+       write_u8(fp, bits >> 8);
+      if (bitmaps)
+       write_u8(fp, bits);
+
+      write_id(fp, needid[s->name].need);
+      write_id(fp, needid[s->arch].need);
+      write_id(fp, needid[s->evr].need);
+
+      if (s->provides)
+        write_idarray(fp, pool, needid, s->provides);
+      if (s->obsoletes)
+        write_idarray(fp, pool, needid, s->obsoletes);
+      if (s->conflicts)
+        write_idarray(fp, pool, needid, s->conflicts);
+      if (s->requires)
+        write_idarray(fp, pool, needid, s->requires);
+      if (s->recommends)
+        write_idarray(fp, pool, needid, s->recommends);
+      if (s->suggests)
+        write_idarray(fp, pool, needid, s->suggests);
+      if (s->supplements)
+        write_idarray(fp, pool, needid, s->supplements);
+      if (s->enhances)
+        write_idarray(fp, pool, needid, s->enhances);
+      if (s->freshens)
+        write_idarray(fp, pool, needid, s->freshens);
+      if (source->rpmdbid)
+        write_u32(fp, source->rpmdbid[i]);
+    }
+
+  free(needid);
+}
+
+// EOF
diff --git a/tools/source_write.h b/tools/source_write.h
new file mode 100644 (file)
index 0000000..3ab230f
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * source_write.h
+ *
+ */
+
+#ifndef SOURCE_WRITE_H
+#define SOURCE_WRITE_H
+
+#include <stdio.h>
+
+#include "pool.h"
+#include "source.h"
+
+extern void pool_writesource(Pool *pool, Source *source, FILE *fp);
+
+#endif
diff --git a/tools/susetags2solv.c b/tools/susetags2solv.c
new file mode 100644 (file)
index 0000000..764a69c
--- /dev/null
@@ -0,0 +1,20 @@
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "source_susetags.h"
+#include "source_write.h"
+
+int
+main(int argc, char **argv)
+{
+  Pool *pool = pool_create();
+  Source *source = pool_addsource_susetags(pool, stdin);
+  pool_writesource(pool, source, stdout);
+  pool_free(pool);
+  exit(0);
+}