From 8f8a9ed5192a6737d63364029cb05d91f1d0e399 Mon Sep 17 00:00:00 2001 From: Klaus Kaempf Date: Tue, 2 Oct 2007 12:40:07 +0000 Subject: [PATCH 1/1] current state of 'sat-solver' --- INSTALL | 26 + Makefile.am | 8 + README | 26 + bootstrap | 18 + configure.in | 137 +++ src/Makefile.am | 29 + src/bitmap.c | 36 + src/bitmap.h | 27 + src/evr.c | 138 +++ src/evr.h | 14 + src/hash.h | 61 ++ src/pool.c | 400 ++++++++ src/pool.h | 127 +++ src/poolarch.c | 84 ++ src/poolarch.h | 8 + src/poolid.c | 238 +++++ src/poolid.h | 31 + src/poolid_private.h | 17 + src/pooltypes.h | 21 + src/queue.c | 94 ++ src/queue.h | 30 + src/solvable.h | 32 + src/solver.c | 2513 +++++++++++++++++++++++++++++++++++++++++++++++ src/solver.h | 105 ++ src/source.c | 283 ++++++ src/source.h | 30 + src/source_solv.c | 588 +++++++++++ src/source_solv.h | 14 + src/util.c | 74 ++ src/util.h | 16 + tools/Makefile.am | 51 + tools/dumpsolv.c | 55 ++ tools/helix2solv.c | 29 + tools/patchxml2solv.c | 20 + tools/rpmdb2solv.c | 51 + tools/rpmmd2solv.c | 20 + tools/source_helix.c | 772 +++++++++++++++ tools/source_helix.h | 15 + tools/source_patchxml.c | 523 ++++++++++ tools/source_patchxml.h | 1 + tools/source_rpmdb.c | 860 ++++++++++++++++ tools/source_rpmdb.h | 1 + tools/source_rpmmd.c | 503 ++++++++++ tools/source_rpmmd.h | 1 + tools/source_susetags.c | 352 +++++++ tools/source_susetags.h | 1 + tools/source_write.c | 400 ++++++++ tools/source_write.h | 16 + tools/susetags2solv.c | 20 + 49 files changed, 8916 insertions(+) create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 README create mode 100755 bootstrap create mode 100644 configure.in create mode 100644 src/Makefile.am create mode 100644 src/bitmap.c create mode 100644 src/bitmap.h create mode 100644 src/evr.c create mode 100644 src/evr.h create mode 100644 src/hash.h create mode 100644 src/pool.c create mode 100644 src/pool.h create mode 100644 src/poolarch.c create mode 100644 src/poolarch.h create mode 100644 src/poolid.c create mode 100644 src/poolid.h create mode 100644 src/poolid_private.h create mode 100644 src/pooltypes.h create mode 100644 src/queue.c create mode 100644 src/queue.h create mode 100644 src/solvable.h create mode 100644 src/solver.c create mode 100644 src/solver.h create mode 100644 src/source.c create mode 100644 src/source.h create mode 100644 src/source_solv.c create mode 100644 src/source_solv.h create mode 100644 src/util.c create mode 100644 src/util.h create mode 100644 tools/Makefile.am create mode 100644 tools/dumpsolv.c create mode 100644 tools/helix2solv.c create mode 100644 tools/patchxml2solv.c create mode 100644 tools/rpmdb2solv.c create mode 100644 tools/rpmmd2solv.c create mode 100644 tools/source_helix.c create mode 100644 tools/source_helix.h create mode 100644 tools/source_patchxml.c create mode 100644 tools/source_patchxml.h create mode 100644 tools/source_rpmdb.c create mode 100644 tools/source_rpmdb.h create mode 100644 tools/source_rpmmd.c create mode 100644 tools/source_rpmmd.h create mode 100644 tools/source_susetags.c create mode 100644 tools/source_susetags.h create mode 100644 tools/source_write.c create mode 100644 tools/source_write.h create mode 100644 tools/susetags2solv.c 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 +#include + +#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 +#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 +#include +#include +#include + +#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[] = { + "", // 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 +#include +#include + +#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 +#include +#include + +#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 +#include + +#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 +#include +#include +#include + +#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: + * Updates: p > 0, d > 0 (A|B1|B2|...) d: + * 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 +#include +#include + +#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 +#include +#include +#include + +#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 +#include +#include +#include + +#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 +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 : */ + { + 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 */ + + 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 + * + * + */ + +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 ':' */ + 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 + * + * + * 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 +#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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#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 + +#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 +#include +#include +#include +#include +#include + +#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); +} -- 2.7.4