summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--INSTALL26
-rw-r--r--Makefile.am8
-rw-r--r--README26
-rwxr-xr-xbootstrap18
-rw-r--r--configure.in137
-rw-r--r--src/Makefile.am29
-rw-r--r--src/bitmap.c36
-rw-r--r--src/bitmap.h27
-rw-r--r--src/evr.c138
-rw-r--r--src/evr.h14
-rw-r--r--src/hash.h61
-rw-r--r--src/pool.c400
-rw-r--r--src/pool.h127
-rw-r--r--src/poolarch.c84
-rw-r--r--src/poolarch.h8
-rw-r--r--src/poolid.c238
-rw-r--r--src/poolid.h31
-rw-r--r--src/poolid_private.h17
-rw-r--r--src/pooltypes.h21
-rw-r--r--src/queue.c94
-rw-r--r--src/queue.h30
-rw-r--r--src/solvable.h32
-rw-r--r--src/solver.c2513
-rw-r--r--src/solver.h105
-rw-r--r--src/source.c283
-rw-r--r--src/source.h30
-rw-r--r--src/source_solv.c588
-rw-r--r--src/source_solv.h14
-rw-r--r--src/util.c74
-rw-r--r--src/util.h16
-rw-r--r--tools/Makefile.am51
-rw-r--r--tools/dumpsolv.c55
-rw-r--r--tools/helix2solv.c29
-rw-r--r--tools/patchxml2solv.c20
-rw-r--r--tools/rpmdb2solv.c51
-rw-r--r--tools/rpmmd2solv.c20
-rw-r--r--tools/source_helix.c772
-rw-r--r--tools/source_helix.h15
-rw-r--r--tools/source_patchxml.c523
-rw-r--r--tools/source_patchxml.h1
-rw-r--r--tools/source_rpmdb.c860
-rw-r--r--tools/source_rpmdb.h1
-rw-r--r--tools/source_rpmmd.c503
-rw-r--r--tools/source_rpmmd.h1
-rw-r--r--tools/source_susetags.c352
-rw-r--r--tools/source_susetags.h1
-rw-r--r--tools/source_write.c400
-rw-r--r--tools/source_write.h16
-rw-r--r--tools/susetags2solv.c20
49 files changed, 8916 insertions, 0 deletions
diff --git a/INSTALL b/INSTALL
new file mode 100644
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
index 0000000..4cf1e17
--- /dev/null
+++ b/Makefile.am
@@ -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
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
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
index 0000000..9896eae
--- /dev/null
+++ b/configure.in
@@ -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
index 0000000..ea3d4d4
--- /dev/null
+++ b/src/Makefile.am
@@ -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
index 0000000..e8ce806
--- /dev/null
+++ b/src/bitmap.c
@@ -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
index 0000000..5a73d24
--- /dev/null
+++ b/src/bitmap.h
@@ -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
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
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
index 0000000..f2534e9
--- /dev/null
+++ b/src/hash.h
@@ -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
index 0000000..6a4a20f
--- /dev/null
+++ b/src/pool.c
@@ -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
index 0000000..1a11e66
--- /dev/null
+++ b/src/pool.h
@@ -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
index 0000000..ec41013
--- /dev/null
+++ b/src/poolarch.c
@@ -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
index 0000000..cd4f044
--- /dev/null
+++ b/src/poolarch.h
@@ -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
index 0000000..a598514
--- /dev/null
+++ b/src/poolid.c
@@ -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
index 0000000..087d113
--- /dev/null
+++ b/src/poolid.h
@@ -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
index 0000000..c2e4807
--- /dev/null
+++ b/src/poolid_private.h
@@ -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
index 0000000..3231b07
--- /dev/null
+++ b/src/pooltypes.h
@@ -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
index 0000000..1b52a8c
--- /dev/null
+++ b/src/queue.c
@@ -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
index 0000000..dd70443
--- /dev/null
+++ b/src/queue.h
@@ -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
index 0000000..aaecd71
--- /dev/null
+++ b/src/solvable.h
@@ -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
index 0000000..4a875b8
--- /dev/null
+++ b/src/solver.c
@@ -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
index 0000000..776978c
--- /dev/null
+++ b/src/solver.h
@@ -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
index 0000000..e3582c6
--- /dev/null
+++ b/src/source.c
@@ -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
index 0000000..a5b1a99
--- /dev/null
+++ b/src/source.h
@@ -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
index 0000000..06d0d81
--- /dev/null
+++ b/src/source_solv.c
@@ -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
index 0000000..33f40c1
--- /dev/null
+++ b/src/source_solv.h
@@ -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
index 0000000..4b95036
--- /dev/null
+++ b/src/util.c
@@ -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
index 0000000..1b90cb3
--- /dev/null
+++ b/src/util.h
@@ -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
index 0000000..d237497
--- /dev/null
+++ b/tools/Makefile.am
@@ -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
index 0000000..e4d66c3
--- /dev/null
+++ b/tools/dumpsolv.c
@@ -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
index 0000000..5abd8b4
--- /dev/null
+++ b/tools/helix2solv.c
@@ -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
index 0000000..5fb549c
--- /dev/null
+++ b/tools/patchxml2solv.c
@@ -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
index 0000000..e4239fb
--- /dev/null
+++ b/tools/rpmdb2solv.c
@@ -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
index 0000000..9ca1ba2
--- /dev/null
+++ b/tools/rpmmd2solv.c
@@ -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
index 0000000..8c367aa
--- /dev/null
+++ b/tools/source_helix.c
@@ -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
index 0000000..dfcf45e
--- /dev/null
+++ b/tools/source_helix.h
@@ -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
index 0000000..413b061
--- /dev/null
+++ b/tools/source_patchxml.c
@@ -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
index 0000000..75d8aa3
--- /dev/null
+++ b/tools/source_patchxml.h
@@ -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
index 0000000..b946bcb
--- /dev/null
+++ b/tools/source_rpmdb.c
@@ -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
index 0000000..d17d418
--- /dev/null
+++ b/tools/source_rpmdb.h
@@ -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
index 0000000..709d567
--- /dev/null
+++ b/tools/source_rpmmd.c
@@ -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
index 0000000..38cc00d
--- /dev/null
+++ b/tools/source_rpmmd.h
@@ -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
index 0000000..b4847e9
--- /dev/null
+++ b/tools/source_susetags.c
@@ -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
index 0000000..3938e7c
--- /dev/null
+++ b/tools/source_susetags.h
@@ -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
index 0000000..2cac212
--- /dev/null
+++ b/tools/source_write.c
@@ -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
index 0000000..3ab230f
--- /dev/null
+++ b/tools/source_write.h
@@ -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
index 0000000..764a69c
--- /dev/null
+++ b/tools/susetags2solv.c
@@ -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);
+}